Commit 593e0a0eb65ac4498e30424b230366c3b286c9e9
1 parent
3ac09c86
ajout fichiers projet
Showing
228 changed files
with
14305 additions
and
0 deletions
Show diff stats
Too many changes.
To preserve performance only 100 of 228 files are displayed.
No preview for this file type
WiimoteWhiteboard/src/apple/dts/samplecode/osxadapter/OSXAdapter.java
0 → 100644
... | ... | @@ -0,0 +1,152 @@ |
1 | +package apple.dts.samplecode.osxadapter; | |
2 | + | |
3 | +import java.lang.reflect.InvocationHandler; | |
4 | +import java.lang.reflect.InvocationTargetException; | |
5 | +import java.lang.reflect.Method; | |
6 | +import java.lang.reflect.Proxy; | |
7 | + | |
8 | + | |
9 | +public class OSXAdapter implements InvocationHandler { | |
10 | + | |
11 | + protected Object targetObject; | |
12 | + protected Method targetMethod; | |
13 | + protected String proxySignature; | |
14 | + | |
15 | + static Object macOSXApplication; | |
16 | + | |
17 | + // Pass this method an Object and Method equipped to perform application shutdown logic | |
18 | + // The method passed should return a boolean stating whether or not the quit should occur | |
19 | + public static void setQuitHandler(Object target, Method quitHandler) { | |
20 | + setHandler(new OSXAdapter("handleQuit", target, quitHandler)); | |
21 | + } | |
22 | + | |
23 | + // Pass this method an Object and Method equipped to display application info | |
24 | + // They will be called when the About menu item is selected from the application menu | |
25 | + public static void setAboutHandler(Object target, Method aboutHandler) { | |
26 | + boolean enableAboutMenu = (target != null && aboutHandler != null); | |
27 | + if (enableAboutMenu) { | |
28 | + setHandler(new OSXAdapter("handleAbout", target, aboutHandler)); | |
29 | + } | |
30 | + // If we're setting a handler, enable the About menu item by calling | |
31 | + // com.apple.eawt.Application reflectively | |
32 | + try { | |
33 | + Method enableAboutMethod = macOSXApplication.getClass().getDeclaredMethod("setEnabledAboutMenu", new Class[] { boolean.class }); | |
34 | + enableAboutMethod.invoke(macOSXApplication, new Object[] { Boolean.valueOf(enableAboutMenu) }); | |
35 | + } catch (Exception ex) { | |
36 | + System.err.println("OSXAdapter could not access the About Menu"); | |
37 | + ex.printStackTrace(); | |
38 | + } | |
39 | + } | |
40 | + | |
41 | + // Pass this method an Object and a Method equipped to display application options | |
42 | + // They will be called when the Preferences menu item is selected from the application menu | |
43 | + public static void setPreferencesHandler(Object target, Method prefsHandler) { | |
44 | + boolean enablePrefsMenu = (target != null && prefsHandler != null); | |
45 | + if (enablePrefsMenu) { | |
46 | + setHandler(new OSXAdapter("handlePreferences", target, prefsHandler)); | |
47 | + } | |
48 | + // If we're setting a handler, enable the Preferences menu item by calling | |
49 | + // com.apple.eawt.Application reflectively | |
50 | + try { | |
51 | + Method enablePrefsMethod = macOSXApplication.getClass().getDeclaredMethod("setEnabledPreferencesMenu", new Class[] { boolean.class }); | |
52 | + enablePrefsMethod.invoke(macOSXApplication, new Object[] { Boolean.valueOf(enablePrefsMenu) }); | |
53 | + } catch (Exception ex) { | |
54 | + System.err.println("OSXAdapter could not access the About Menu"); | |
55 | + ex.printStackTrace(); | |
56 | + } | |
57 | + } | |
58 | + | |
59 | + // Pass this method an Object and a Method equipped to handle document events from the Finder | |
60 | + // Documents are registered with the Finder via the CFBundleDocumentTypes dictionary in the | |
61 | + // application bundle's Info.plist | |
62 | + public static void setFileHandler(Object target, Method fileHandler) { | |
63 | + setHandler(new OSXAdapter("handleOpenFile", target, fileHandler) { | |
64 | + // Override OSXAdapter.callTarget to send information on the | |
65 | + // file to be opened | |
66 | + @Override | |
67 | + public boolean callTarget(Object appleEvent) { | |
68 | + if (appleEvent != null) { | |
69 | + try { | |
70 | + Method getFilenameMethod = appleEvent.getClass().getDeclaredMethod("getFilename", (Class[])null); | |
71 | + String filename = (String) getFilenameMethod.invoke(appleEvent, (Object[])null); | |
72 | + this.targetMethod.invoke(this.targetObject, new Object[] { filename }); | |
73 | + } catch (Exception ex) { | |
74 | + | |
75 | + } | |
76 | + } | |
77 | + return true; | |
78 | + } | |
79 | + }); | |
80 | + } | |
81 | + | |
82 | + // setHandler creates a Proxy object from the passed OSXAdapter and adds it as an ApplicationListener | |
83 | + public static void setHandler(OSXAdapter adapter) { | |
84 | + try { | |
85 | + Class<?> applicationClass = Class.forName("com.apple.eawt.Application"); | |
86 | + if (macOSXApplication == null) { | |
87 | + macOSXApplication = applicationClass.getConstructor((Class[])null).newInstance((Object[])null); | |
88 | + } | |
89 | + Class<?> applicationListenerClass = Class.forName("com.apple.eawt.ApplicationListener"); | |
90 | + Method addListenerMethod = applicationClass.getDeclaredMethod("addApplicationListener", new Class[] { applicationListenerClass }); | |
91 | + // Create a proxy object around this handler that can be reflectively added as an Apple ApplicationListener | |
92 | + Object osxAdapterProxy = Proxy.newProxyInstance(OSXAdapter.class.getClassLoader(), new Class[] { applicationListenerClass }, adapter); | |
93 | + addListenerMethod.invoke(macOSXApplication, new Object[] { osxAdapterProxy }); | |
94 | + } catch (ClassNotFoundException cnfe) { | |
95 | + System.err.println("This version of Mac OS X does not support the Apple EAWT. ApplicationEvent handling has been disabled (" + cnfe + ")"); | |
96 | + } catch (Exception ex) { // Likely a NoSuchMethodException or an IllegalAccessException loading/invoking eawt.Application methods | |
97 | + System.err.println("Mac OS X Adapter could not talk to EAWT:"); | |
98 | + ex.printStackTrace(); | |
99 | + } | |
100 | + } | |
101 | + | |
102 | + // Each OSXAdapter has the name of the EAWT method it intends to listen for (handleAbout, for example), | |
103 | + // the Object that will ultimately perform the task, and the Method to be called on that Object | |
104 | + protected OSXAdapter(String proxySignature, Object target, Method handler) { | |
105 | + this.proxySignature = proxySignature; | |
106 | + this.targetObject = target; | |
107 | + this.targetMethod = handler; | |
108 | + } | |
109 | + | |
110 | + // Override this method to perform any operations on the event | |
111 | + // that comes with the various callbacks | |
112 | + // See setFileHandler above for an example | |
113 | + public boolean callTarget(Object appleEvent) throws InvocationTargetException, IllegalAccessException { | |
114 | + Object result = targetMethod.invoke(targetObject, (Object[])null); | |
115 | + if (result == null) { | |
116 | + return true; | |
117 | + } | |
118 | + return Boolean.valueOf(result.toString()).booleanValue(); | |
119 | + } | |
120 | + | |
121 | + // InvocationHandler implementation | |
122 | + // This is the entry point for our proxy object; it is called every time an ApplicationListener method is invoked | |
123 | + public Object invoke (Object proxy, Method method, Object[] args) throws Throwable { | |
124 | + if (isCorrectMethod(method, args)) { | |
125 | + boolean handled = callTarget(args[0]); | |
126 | + setApplicationEventHandled(args[0], handled); | |
127 | + } | |
128 | + // All of the ApplicationListener methods are void; return null regardless of what happens | |
129 | + return null; | |
130 | + } | |
131 | + | |
132 | + // Compare the method that was called to the intended method when the OSXAdapter instance was created | |
133 | + // (e.g. handleAbout, handleQuit, handleOpenFile, etc.) | |
134 | + protected boolean isCorrectMethod(Method method, Object[] args) { | |
135 | + return (targetMethod != null && proxySignature.equals(method.getName()) && args.length == 1); | |
136 | + } | |
137 | + | |
138 | + // It is important to mark the ApplicationEvent as handled and cancel the default behavior | |
139 | + // This method checks for a boolean result from the proxy method and sets the event accordingly | |
140 | + protected void setApplicationEventHandled(Object event, boolean handled) { | |
141 | + if (event != null) { | |
142 | + try { | |
143 | + Method setHandledMethod = event.getClass().getDeclaredMethod("setHandled", new Class[] { boolean.class }); | |
144 | + // If the target method returns a boolean, use that as a hint | |
145 | + setHandledMethod.invoke(event, new Object[] { Boolean.valueOf(handled) }); | |
146 | + } catch (Exception ex) { | |
147 | + System.err.println("OSXAdapter was unable to handle an ApplicationEvent: " + event); | |
148 | + ex.printStackTrace(); | |
149 | + } | |
150 | + } | |
151 | + } | |
152 | +} | ... | ... |
WiimoteWhiteboard/src/apple/dts/samplecode/osxadapter/OSXAdapter.java~
0 → 100644
... | ... | @@ -0,0 +1,152 @@ |
1 | +package apple.dts.samplecode.osxadapter; | |
2 | + | |
3 | +import java.lang.reflect.InvocationHandler; | |
4 | +import java.lang.reflect.InvocationTargetException; | |
5 | +import java.lang.reflect.Method; | |
6 | +import java.lang.reflect.Proxy; | |
7 | + | |
8 | + | |
9 | +public class OSXAdapter implements InvocationHandler { | |
10 | + | |
11 | + protected Object targetObject; | |
12 | + protected Method targetMethod; | |
13 | + protected String proxySignature; | |
14 | + | |
15 | + static Object macOSXApplication; | |
16 | + | |
17 | + // Pass this method an Object and Method equipped to perform application shutdown logic | |
18 | + // The method passed should return a boolean stating whether or not the quit should occur | |
19 | + public static void setQuitHandler(Object target, Method quitHandler) { | |
20 | + setHandler(new OSXAdapter("handleQuit", target, quitHandler)); | |
21 | + } | |
22 | + | |
23 | + // Pass this method an Object and Method equipped to display application info | |
24 | + // They will be called when the About menu item is selected from the application menu | |
25 | + public static void setAboutHandler(Object target, Method aboutHandler) { | |
26 | + boolean enableAboutMenu = (target != null && aboutHandler != null); | |
27 | + if (enableAboutMenu) { | |
28 | + setHandler(new OSXAdapter("handleAbout", target, aboutHandler)); | |
29 | + } | |
30 | + // If we're setting a handler, enable the About menu item by calling | |
31 | + // com.apple.eawt.Application reflectively | |
32 | + try { | |
33 | + Method enableAboutMethod = macOSXApplication.getClass().getDeclaredMethod("setEnabledAboutMenu", new Class[] { boolean.class }); | |
34 | + enableAboutMethod.invoke(macOSXApplication, new Object[] { Boolean.valueOf(enableAboutMenu) }); | |
35 | + } catch (Exception ex) { | |
36 | + System.err.println("OSXAdapter could not access the About Menu"); | |
37 | + ex.printStackTrace(); | |
38 | + } | |
39 | + } | |
40 | + | |
41 | + // Pass this method an Object and a Method equipped to display application options | |
42 | + // They will be called when the Preferences menu item is selected from the application menu | |
43 | + public static void setPreferencesHandler(Object target, Method prefsHandler) { | |
44 | + boolean enablePrefsMenu = (target != null && prefsHandler != null); | |
45 | + if (enablePrefsMenu) { | |
46 | + setHandler(new OSXAdapter("handlePreferences", target, prefsHandler)); | |
47 | + } | |
48 | + // If we're setting a handler, enable the Preferences menu item by calling | |
49 | + // com.apple.eawt.Application reflectively | |
50 | + try { | |
51 | + Method enablePrefsMethod = macOSXApplication.getClass().getDeclaredMethod("setEnabledPreferencesMenu", new Class[] { boolean.class }); | |
52 | + enablePrefsMethod.invoke(macOSXApplication, new Object[] { Boolean.valueOf(enablePrefsMenu) }); | |
53 | + } catch (Exception ex) { | |
54 | + System.err.println("OSXAdapter could not access the About Menu"); | |
55 | + ex.printStackTrace(); | |
56 | + } | |
57 | + } | |
58 | + | |
59 | + // Pass this method an Object and a Method equipped to handle document events from the Finder | |
60 | + // Documents are registered with the Finder via the CFBundleDocumentTypes dictionary in the | |
61 | + // application bundle's Info.plist | |
62 | + public static void setFileHandler(Object target, Method fileHandler) { | |
63 | + setHandler(new OSXAdapter("handleOpenFile", target, fileHandler) { | |
64 | + // Override OSXAdapter.callTarget to send information on the | |
65 | + // file to be opened | |
66 | + @Override | |
67 | + public boolean callTarget(Object appleEvent) { | |
68 | + if (appleEvent != null) { | |
69 | + try { | |
70 | + Method getFilenameMethod = appleEvent.getClass().getDeclaredMethod("getFilename", (Class[])null); | |
71 | + String filename = (String) getFilenameMethod.invoke(appleEvent, (Object[])null); | |
72 | + this.targetMethod.invoke(this.targetObject, new Object[] { filename }); | |
73 | + } catch (Exception ex) { | |
74 | + | |
75 | + } | |
76 | + } | |
77 | + return true; | |
78 | + } | |
79 | + }); | |
80 | + } | |
81 | + | |
82 | + // setHandler creates a Proxy object from the passed OSXAdapter and adds it as an ApplicationListener | |
83 | + public static void setHandler(OSXAdapter adapter) { | |
84 | + try { | |
85 | + Class<?> applicationClass = Class.forName("com.apple.eawt.Application"); | |
86 | + if (macOSXApplication == null) { | |
87 | + macOSXApplication = applicationClass.getConstructor((Class[])null).newInstance((Object[])null); | |
88 | + } | |
89 | + Class<?> applicationListenerClass = Class.forName("com.apple.eawt.ApplicationListener"); | |
90 | + Method addListenerMethod = applicationClass.getDeclaredMethod("addApplicationListener", new Class[] { applicationListenerClass }); | |
91 | + // Create a proxy object around this handler that can be reflectively added as an Apple ApplicationListener | |
92 | + Object osxAdapterProxy = Proxy.newProxyInstance(OSXAdapter.class.getClassLoader(), new Class[] { applicationListenerClass }, adapter); | |
93 | + addListenerMethod.invoke(macOSXApplication, new Object[] { osxAdapterProxy }); | |
94 | + } catch (ClassNotFoundException cnfe) { | |
95 | + System.err.println("This version of Mac OS X does not support the Apple EAWT. ApplicationEvent handling has been disabled (" + cnfe + ")"); | |
96 | + } catch (Exception ex) { // Likely a NoSuchMethodException or an IllegalAccessException loading/invoking eawt.Application methods | |
97 | + System.err.println("Mac OS X Adapter could not talk to EAWT:"); | |
98 | + ex.printStackTrace(); | |
99 | + } | |
100 | + } | |
101 | + | |
102 | + // Each OSXAdapter has the name of the EAWT method it intends to listen for (handleAbout, for example), | |
103 | + // the Object that will ultimately perform the task, and the Method to be called on that Object | |
104 | + protected OSXAdapter(String proxySignature, Object target, Method handler) { | |
105 | + this.proxySignature = proxySignature; | |
106 | + this.targetObject = target; | |
107 | + this.targetMethod = handler; | |
108 | + } | |
109 | + | |
110 | + // Override this method to perform any operations on the event | |
111 | + // that comes with the various callbacks | |
112 | + // See setFileHandler above for an example | |
113 | + public boolean callTarget(Object appleEvent) throws InvocationTargetException, IllegalAccessException { | |
114 | + Object result = targetMethod.invoke(targetObject, (Object[])null); | |
115 | + if (result == null) { | |
116 | + return true; | |
117 | + } | |
118 | + return Boolean.valueOf(result.toString()).booleanValue(); | |
119 | + } | |
120 | + | |
121 | + // InvocationHandler implementation | |
122 | + // This is the entry point for our proxy object; it is called every time an ApplicationListener method is invoked | |
123 | + public Object invoke (Object proxy, Method method, Object[] args) throws Throwable { | |
124 | + if (isCorrectMethod(method, args)) { | |
125 | + boolean handled = callTarget(args[0]); | |
126 | + setApplicationEventHandled(args[0], handled); | |
127 | + } | |
128 | + // All of the ApplicationListener methods are void; return null regardless of what happens | |
129 | + return null; | |
130 | + } | |
131 | + | |
132 | + // Compare the method that was called to the intended method when the OSXAdapter instance was created | |
133 | + // (e.g. handleAbout, handleQuit, handleOpenFile, etc.) | |
134 | + protected boolean isCorrectMethod(Method method, Object[] args) { | |
135 | + return (targetMethod != null && proxySignature.equals(method.getName()) && args.length == 1); | |
136 | + } | |
137 | + | |
138 | + // It is important to mark the ApplicationEvent as handled and cancel the default behavior | |
139 | + // This method checks for a boolean result from the proxy method and sets the event accordingly | |
140 | + protected void setApplicationEventHandled(Object event, boolean handled) { | |
141 | + if (event != null) { | |
142 | + try { | |
143 | + Method setHandledMethod = event.getClass().getDeclaredMethod("setHandled", new Class[] { boolean.class }); | |
144 | + // If the target method returns a boolean, use that as a hint | |
145 | + setHandledMethod.invoke(event, new Object[] { Boolean.valueOf(handled) }); | |
146 | + } catch (Exception ex) { | |
147 | + System.err.println("OSXAdapter was unable to handle an ApplicationEvent: " + event); | |
148 | + ex.printStackTrace(); | |
149 | + } | |
150 | + } | |
151 | + } | |
152 | +} | ... | ... |
WiimoteWhiteboard/src/javax/media/jai/PerspectiveTransform.java
0 → 100644
... | ... | @@ -0,0 +1,1095 @@ |
1 | +package javax.media.jai; | |
2 | +import java.awt.geom.AffineTransform; | |
3 | +import java.awt.geom.Point2D; | |
4 | +import java.awt.geom.NoninvertibleTransformException; | |
5 | +import java.io.Serializable; | |
6 | + | |
7 | +public final class PerspectiveTransform implements Cloneable, Serializable { | |
8 | + | |
9 | + private static final double PERSPECTIVE_DIVIDE_EPSILON = 1.0e-10; | |
10 | + | |
11 | + double m00, m01, m02, m10, m11, m12, m20, m21, m22; | |
12 | + | |
13 | + /** Constructs an identity PerspectiveTransform. */ | |
14 | + public PerspectiveTransform() { | |
15 | + m00 = m11 = m22 = 1.0; | |
16 | + m01 = m02 = m10 = m12 = m20 = m21 = 0.0; | |
17 | + } | |
18 | + | |
19 | + /** | |
20 | + * Constructs a new PerspectiveTransform from 9 floats. | |
21 | + */ | |
22 | + public PerspectiveTransform(float m00, float m01, float m02, | |
23 | + float m10, float m11, float m12, | |
24 | + float m20, float m21, float m22) { | |
25 | + this.m00 = m00; | |
26 | + this.m01 = m01; | |
27 | + this.m02 = m02; | |
28 | + this.m10 = m10; | |
29 | + this.m11 = m11; | |
30 | + this.m12 = m12; | |
31 | + this.m20 = m20; | |
32 | + this.m21 = m21; | |
33 | + this.m22 = m22; | |
34 | + } | |
35 | + | |
36 | + /** | |
37 | + * Constructs a new PerspectiveTransform from 9 doubles. | |
38 | + */ | |
39 | + public PerspectiveTransform(double m00, double m01, double m02, | |
40 | + double m10, double m11, double m12, | |
41 | + double m20, double m21, double m22) { | |
42 | + this.m00 = m00; | |
43 | + this.m01 = m01; | |
44 | + this.m02 = m02; | |
45 | + this.m10 = m10; | |
46 | + this.m11 = m11; | |
47 | + this.m12 = m12; | |
48 | + this.m20 = m20; | |
49 | + this.m21 = m21; | |
50 | + this.m22 = m22; | |
51 | + } | |
52 | + | |
53 | + public PerspectiveTransform(float[] flatmatrix) { | |
54 | + if ( flatmatrix == null ) { | |
55 | + throw new IllegalArgumentException(JaiI18N.getString("Generic0")); | |
56 | + } | |
57 | + | |
58 | + m00 = flatmatrix[0]; | |
59 | + m01 = flatmatrix[1]; | |
60 | + m02 = flatmatrix[2]; | |
61 | + m10 = flatmatrix[3]; | |
62 | + m11 = flatmatrix[4]; | |
63 | + m12 = flatmatrix[5]; | |
64 | + m20 = flatmatrix[6]; | |
65 | + m21 = flatmatrix[7]; | |
66 | + m22 = flatmatrix[8]; | |
67 | + } | |
68 | + | |
69 | + public PerspectiveTransform(float[][] matrix) { | |
70 | + if ( matrix == null ) { | |
71 | + throw new IllegalArgumentException(JaiI18N.getString("Generic0")); | |
72 | + } | |
73 | + | |
74 | + m00 = matrix[0][0]; | |
75 | + m01 = matrix[0][1]; | |
76 | + m02 = matrix[0][2]; | |
77 | + m10 = matrix[1][0]; | |
78 | + m11 = matrix[1][1]; | |
79 | + m12 = matrix[1][2]; | |
80 | + m20 = matrix[2][0]; | |
81 | + m21 = matrix[2][1]; | |
82 | + m22 = matrix[2][2]; | |
83 | + } | |
84 | + | |
85 | + public PerspectiveTransform(double[] flatmatrix) { | |
86 | + if ( flatmatrix == null ) { | |
87 | + throw new IllegalArgumentException(JaiI18N.getString("Generic0")); | |
88 | + } | |
89 | + | |
90 | + m00 = flatmatrix[0]; | |
91 | + m01 = flatmatrix[1]; | |
92 | + m02 = flatmatrix[2]; | |
93 | + m10 = flatmatrix[3]; | |
94 | + m11 = flatmatrix[4]; | |
95 | + m12 = flatmatrix[5]; | |
96 | + m20 = flatmatrix[6]; | |
97 | + m21 = flatmatrix[7]; | |
98 | + m22 = flatmatrix[8]; | |
99 | + } | |
100 | + | |
101 | + /** | |
102 | + * Constructs a new PerspectiveTransform from a two-dimensional | |
103 | + * array of doubles. | |
104 | + */ | |
105 | + public PerspectiveTransform(double[][] matrix) { | |
106 | + if ( matrix == null ) { | |
107 | + throw new IllegalArgumentException(JaiI18N.getString("Generic0")); | |
108 | + } | |
109 | + | |
110 | + m00 = matrix[0][0]; | |
111 | + m01 = matrix[0][1]; | |
112 | + m02 = matrix[0][2]; | |
113 | + m10 = matrix[1][0]; | |
114 | + m11 = matrix[1][1]; | |
115 | + m12 = matrix[1][2]; | |
116 | + m20 = matrix[2][0]; | |
117 | + m21 = matrix[2][1]; | |
118 | + m22 = matrix[2][2]; | |
119 | + } | |
120 | + | |
121 | + /** | |
122 | + * Constructs a new PerspectiveTransform with the same effect | |
123 | + * as an existing AffineTransform. | |
124 | + * @throws IllegalArgumentException if transform is null | |
125 | + */ | |
126 | + public PerspectiveTransform(AffineTransform transform) { | |
127 | + if ( transform == null ) { | |
128 | + throw new IllegalArgumentException(JaiI18N.getString("Generic0")); | |
129 | + } | |
130 | + | |
131 | + m00 = transform.getScaleX(); | |
132 | + m01 = transform.getShearX(); | |
133 | + m02 = transform.getTranslateX(); | |
134 | + m10 = transform.getShearY(); | |
135 | + m11 = transform.getScaleY(); | |
136 | + m12 = transform.getTranslateY(); | |
137 | + m20 = 0.0; | |
138 | + m21 = 0.0; | |
139 | + m22 = 1.0; | |
140 | + } | |
141 | + | |
142 | + /** | |
143 | + * Replaces the matrix with its adjoint. | |
144 | + */ | |
145 | + private final void makeAdjoint() { | |
146 | + double m00p = m11*m22 - m12*m21; | |
147 | + double m01p = m12*m20 - m10*m22; | |
148 | + double m02p = m10*m21 - m11*m20; | |
149 | + double m10p = m02*m21 - m01*m22; | |
150 | + double m11p = m00*m22 - m02*m20; | |
151 | + double m12p = m01*m20 - m00*m21; | |
152 | + double m20p = m01*m12 - m02*m11; | |
153 | + double m21p = m02*m10 - m00*m12; | |
154 | + double m22p = m00*m11 - m01*m10; | |
155 | + | |
156 | + m00 = m00p; | |
157 | + m01 = m10p; | |
158 | + m02 = m20p; | |
159 | + m10 = m01p; | |
160 | + m11 = m11p; | |
161 | + m12 = m21p; | |
162 | + m20 = m02p; | |
163 | + m21 = m12p; | |
164 | + m22 = m22p; | |
165 | + } | |
166 | + | |
167 | + private final void normalize() { | |
168 | + double invscale = 1.0/m22; | |
169 | + m00 *= invscale; | |
170 | + m01 *= invscale; | |
171 | + m02 *= invscale; | |
172 | + m10 *= invscale; | |
173 | + m11 *= invscale; | |
174 | + m12 *= invscale; | |
175 | + m20 *= invscale; | |
176 | + m21 *= invscale; | |
177 | + m22 = 1.0; | |
178 | + } | |
179 | + | |
180 | + private static final void getSquareToQuad(double x0, double y0, | |
181 | + double x1, double y1, | |
182 | + double x2, double y2, | |
183 | + double x3, double y3, | |
184 | + PerspectiveTransform tx) { | |
185 | + double dx3 = x0 - x1 + x2 - x3; | |
186 | + double dy3 = y0 - y1 + y2 - y3; | |
187 | + | |
188 | + tx.m22 = 1.0F; | |
189 | + | |
190 | + if ((dx3 == 0.0F) && (dy3 == 0.0F)) { | |
191 | + tx.m00 = x1 - x0; | |
192 | + tx.m01 = x2 - x1; | |
193 | + tx.m02 = x0; | |
194 | + tx.m10 = y1 - y0; | |
195 | + tx.m11 = y2 - y1; | |
196 | + tx.m12 = y0; | |
197 | + tx.m20 = 0.0F; | |
198 | + tx.m21 = 0.0F; | |
199 | + } else { | |
200 | + double dx1 = x1 - x2; | |
201 | + double dy1 = y1 - y2; | |
202 | + double dx2 = x3 - x2; | |
203 | + double dy2 = y3 - y2; | |
204 | + | |
205 | + double invdet = 1.0F/(dx1*dy2 - dx2*dy1); | |
206 | + tx.m20 = (dx3*dy2 - dx2*dy3)*invdet; | |
207 | + tx.m21 = (dx1*dy3 - dx3*dy1)*invdet; | |
208 | + tx.m00 = x1 - x0 + tx.m20*x1; | |
209 | + tx.m01 = x3 - x0 + tx.m21*x3; | |
210 | + tx.m02 = x0; | |
211 | + tx.m10 = y1 - y0 + tx.m20*y1; | |
212 | + tx.m11 = y3 - y0 + tx.m21*y3; | |
213 | + tx.m12 = y0; | |
214 | + } | |
215 | + } | |
216 | + | |
217 | + public static PerspectiveTransform getSquareToQuad(double x0, double y0, | |
218 | + double x1, double y1, | |
219 | + double x2, double y2, | |
220 | + double x3, double y3) { | |
221 | + PerspectiveTransform tx = new PerspectiveTransform(); | |
222 | + getSquareToQuad(x0, y0, x1, y1, x2, y2, x3, y3, tx); | |
223 | + return tx; | |
224 | + } | |
225 | + | |
226 | + | |
227 | + public static PerspectiveTransform getSquareToQuad(float x0, float y0, | |
228 | + float x1, float y1, | |
229 | + float x2, float y2, | |
230 | + float x3, float y3) { | |
231 | + return getSquareToQuad((double)x0, (double)y0, | |
232 | + (double)x1, (double)y1, | |
233 | + (double)x2, (double)y2, | |
234 | + (double)x3, (double)y3); | |
235 | + } | |
236 | + | |
237 | + | |
238 | + /** | |
239 | + * Creates a PerspectiveTransform that maps an arbitrary | |
240 | + * quadrilateral onto the unit square. | |
241 | + */ | |
242 | + public static PerspectiveTransform getQuadToSquare(double x0, double y0, | |
243 | + double x1, double y1, | |
244 | + double x2, double y2, | |
245 | + double x3, double y3) { | |
246 | + PerspectiveTransform tx = new PerspectiveTransform(); | |
247 | + getSquareToQuad(x0, y0, x1, y1, x2, y2, x3, y3, tx); | |
248 | + tx.makeAdjoint(); | |
249 | + return tx; | |
250 | + } | |
251 | + | |
252 | + /** | |
253 | + * Creates a PerspectiveTransform that maps an arbitrary | |
254 | + * quadrilateral onto the unit square. | |
255 | + */ | |
256 | + public static PerspectiveTransform getQuadToSquare(float x0, float y0, | |
257 | + float x1, float y1, | |
258 | + float x2, float y2, | |
259 | + float x3, float y3) { | |
260 | + return getQuadToSquare((double)x0, (double)y0, | |
261 | + (double)x1, (double)y1, | |
262 | + (double)x2, (double)y2, | |
263 | + (double)x3, (double)y3); | |
264 | + } | |
265 | + | |
266 | + /** | |
267 | + * Creates a PerspectiveTransform that maps an arbitrary | |
268 | + * quadrilateral onto another arbitrary quadrilateral. | |
269 | + */ | |
270 | + public static PerspectiveTransform getQuadToQuad(double x0, double y0, | |
271 | + double x1, double y1, | |
272 | + double x2, double y2, | |
273 | + double x3, double y3, | |
274 | + double x0p, double y0p, | |
275 | + double x1p, double y1p, | |
276 | + double x2p, double y2p, | |
277 | + double x3p, double y3p) { | |
278 | + PerspectiveTransform tx1 = | |
279 | + getQuadToSquare(x0, y0, x1, y1, x2, y2, x3, y3); | |
280 | + | |
281 | + PerspectiveTransform tx2 = | |
282 | + getSquareToQuad(x0p, y0p, x1p, y1p, x2p, y2p, x3p, y3p); | |
283 | + | |
284 | + tx1.concatenate(tx2); | |
285 | + return tx1; | |
286 | + } | |
287 | + | |
288 | + | |
289 | + /** | |
290 | + * Creates a PerspectiveTransform that maps an arbitrary | |
291 | + * quadrilateral onto another arbitrary quadrilateral. | |
292 | + */ | |
293 | + public static PerspectiveTransform getQuadToQuad(float x0, float y0, | |
294 | + float x1, float y1, | |
295 | + float x2, float y2, | |
296 | + float x3, float y3, | |
297 | + float x0p, float y0p, | |
298 | + float x1p, float y1p, | |
299 | + float x2p, float y2p, | |
300 | + float x3p, float y3p) { | |
301 | + return getQuadToQuad((double)x0, (double)y0, | |
302 | + (double)x1, (double)y1, | |
303 | + (double)x2, (double)y2, | |
304 | + (double)x3, (double)y3, | |
305 | + (double)x0p, (double)y0p, | |
306 | + (double)x1p, (double)y1p, | |
307 | + (double)x2p, (double)y2p, | |
308 | + (double)x3p, (double)y3p); | |
309 | + } | |
310 | + | |
311 | + /** | |
312 | + * Returns the determinant of the matrix representation of the | |
313 | + * transform. | |
314 | + */ | |
315 | + public double getDeterminant() { | |
316 | + return ( (m00 * ((m11 * m22) - (m12 * m21))) - | |
317 | + (m01 * ((m10 * m22) - (m12 * m20))) + | |
318 | + (m02 * ((m10 * m21) - (m11 * m20))) ); | |
319 | + | |
320 | + } | |
321 | + | |
322 | + /** | |
323 | + * Retrieves the 9 specifiable values in the 3x3 affine | |
324 | + * transformation matrix into an array of double precision values. | |
325 | + * The values are stored into the array as | |
326 | + * { m00 m01 m02 m10 m11 m12 m20 m21 m22 }. | |
327 | + */ | |
328 | + public double[] getMatrix(double[] flatmatrix) { | |
329 | + if (flatmatrix == null) { | |
330 | + flatmatrix = new double[9]; | |
331 | + } | |
332 | + | |
333 | + flatmatrix[0] = m00; | |
334 | + flatmatrix[1] = m01; | |
335 | + flatmatrix[2] = m02; | |
336 | + flatmatrix[3] = m10; | |
337 | + flatmatrix[4] = m11; | |
338 | + flatmatrix[5] = m12; | |
339 | + flatmatrix[6] = m20; | |
340 | + flatmatrix[7] = m21; | |
341 | + flatmatrix[8] = m22; | |
342 | + | |
343 | + return flatmatrix; | |
344 | + } | |
345 | + | |
346 | + /** | |
347 | + * Retrieves the 9 specifiable values in the 3x3 affine | |
348 | + * transformation matrix into a 2-dimensional array of double | |
349 | + * precision values. The values are stored into the 2-dimensional | |
350 | + * array using the row index as the first subscript and the column | |
351 | + * index as the second. | |
352 | + * | |
353 | + */ | |
354 | + public double[][] getMatrix(double[][] matrix) { | |
355 | + if (matrix == null) { | |
356 | + matrix = new double[3][3]; | |
357 | + } | |
358 | + | |
359 | + matrix[0][0] = m00; | |
360 | + matrix[0][1] = m01; | |
361 | + matrix[0][2] = m02; | |
362 | + matrix[1][0] = m10; | |
363 | + matrix[1][1] = m11; | |
364 | + matrix[1][2] = m12; | |
365 | + matrix[2][0] = m20; | |
366 | + matrix[2][1] = m21; | |
367 | + matrix[2][2] = m22; | |
368 | + | |
369 | + return matrix; | |
370 | + } | |
371 | + | |
372 | + /** | |
373 | + * Concatenates this transform with a translation transformation. | |
374 | + */ | |
375 | + public void translate(double tx, double ty) { | |
376 | + PerspectiveTransform Tx = new PerspectiveTransform(); | |
377 | + Tx.setToTranslation(tx, ty); | |
378 | + concatenate(Tx); | |
379 | + } | |
380 | + | |
381 | + /** | |
382 | + * Concatenates this transform with a rotation transformation. | |
383 | + */ | |
384 | + public void rotate(double theta) { | |
385 | + PerspectiveTransform Tx = new PerspectiveTransform(); | |
386 | + Tx.setToRotation(theta); | |
387 | + concatenate(Tx); | |
388 | + } | |
389 | + | |
390 | + /** | |
391 | + * Concatenates this transform with a translated rotation transformation. | |
392 | + */ | |
393 | + public void rotate(double theta, double x, double y) { | |
394 | + PerspectiveTransform Tx = new PerspectiveTransform(); | |
395 | + Tx.setToRotation(theta, x, y); | |
396 | + concatenate(Tx); | |
397 | + } | |
398 | + | |
399 | + /** | |
400 | + * Concatenates this transform with a scaling transformation. | |
401 | + */ | |
402 | + public void scale(double sx, double sy) { | |
403 | + PerspectiveTransform Tx = new PerspectiveTransform(); | |
404 | + Tx.setToScale(sx, sy); | |
405 | + concatenate(Tx); | |
406 | + } | |
407 | + | |
408 | + /** | |
409 | + * Concatenates this transform with a shearing transformation. | |
410 | + */ | |
411 | + public void shear(double shx, double shy) { | |
412 | + PerspectiveTransform Tx = new PerspectiveTransform(); | |
413 | + Tx.setToShear(shx, shy); | |
414 | + concatenate(Tx); | |
415 | + } | |
416 | + | |
417 | + /** | |
418 | + * Resets this transform to the Identity transform. | |
419 | + */ | |
420 | + public void setToIdentity() { | |
421 | + m00 = m11 = m22 = 1.0; | |
422 | + m01 = m10 = m02 = m20 = m12 = m21 = 0.0; | |
423 | + } | |
424 | + | |
425 | + /** | |
426 | + * Sets this transform to a translation transformation. | |
427 | + */ | |
428 | + public void setToTranslation(double tx, double ty) { | |
429 | + m00 = 1.0; | |
430 | + m01 = 0.0; | |
431 | + m02 = tx; | |
432 | + m10 = 0.0; | |
433 | + m11 = 1.0; | |
434 | + m12 = ty; | |
435 | + m20 = 0.0; | |
436 | + m21 = 0.0; | |
437 | + m22 = 1.0; | |
438 | + } | |
439 | + | |
440 | + /** | |
441 | + * Sets this transform to a rotation transformation. | |
442 | + */ | |
443 | + public void setToRotation(double theta) { | |
444 | + m00 = Math.cos(theta); | |
445 | + m01 = -Math.sin(theta); | |
446 | + m02 = 0.0; | |
447 | + m10 = - m01; | |
448 | + m11 = m00; | |
449 | + m12 = 0.0; | |
450 | + m20 = 0.0; | |
451 | + m21 = 0.0; | |
452 | + m22 = 1.0; | |
453 | + } | |
454 | + | |
455 | + public void setToRotation(double theta, double x, double y) { | |
456 | + setToRotation(theta); | |
457 | + double sin = m10; | |
458 | + double oneMinusCos = 1.0 - m00; | |
459 | + m02 = x * oneMinusCos + y * sin; | |
460 | + m12 = y * oneMinusCos - x * sin; | |
461 | + } | |
462 | + | |
463 | + public void setToScale(double sx, double sy) { | |
464 | + m00 = sx; | |
465 | + m01 = 0.0; | |
466 | + m02 = 0.0; | |
467 | + m10 = 0.0; | |
468 | + m11 = sy; | |
469 | + m12 = 0.0; | |
470 | + m20 = 0.0; | |
471 | + m21 = 0.0; | |
472 | + m22 = 1.0; | |
473 | + } | |
474 | + | |
475 | + /** | |
476 | + * Sets this transform to a shearing transformation | |
477 | + * with shear factors sx and sy. | |
478 | + */ | |
479 | + public void setToShear(double shx, double shy) { | |
480 | + m00 = 1.0; | |
481 | + m01 = shx; | |
482 | + m02 = 0.0; | |
483 | + m10 = shy; | |
484 | + m11 = 1.0; | |
485 | + m12 = 0.0; | |
486 | + m20 = 0.0; | |
487 | + m21 = 0.0; | |
488 | + m22 = 1.0; | |
489 | + } | |
490 | + | |
491 | + /** | |
492 | + * Sets this transform to a given AffineTransform. | |
493 | + * @throws IllegalArgumentException if Tx is null | |
494 | + */ | |
495 | + public void setTransform(AffineTransform Tx) { | |
496 | + if ( Tx == null ) { | |
497 | + throw new IllegalArgumentException(JaiI18N.getString("Generic0")); | |
498 | + } | |
499 | + | |
500 | + m00 = Tx.getScaleX(); | |
501 | + m01 = Tx.getShearX(); | |
502 | + m02 = Tx.getTranslateX(); | |
503 | + m10 = Tx.getShearY(); | |
504 | + m11 = Tx.getScaleY(); | |
505 | + m12 = Tx.getTranslateY(); | |
506 | + m20 = 0.0; | |
507 | + m21 = 0.0; | |
508 | + m22 = 1.0; | |
509 | + } | |
510 | + | |
511 | + /** | |
512 | + * Sets this transform to a given PerspectiveTransform. | |
513 | + * @throws IllegalArgumentException if Tx is null | |
514 | + */ | |
515 | + public void setTransform(PerspectiveTransform Tx) { | |
516 | + if ( Tx == null ) { | |
517 | + throw new IllegalArgumentException(JaiI18N.getString("Generic0")); | |
518 | + } | |
519 | + | |
520 | + m00 = Tx.m00; | |
521 | + m01 = Tx.m01; | |
522 | + m02 = Tx.m02; | |
523 | + m10 = Tx.m10; | |
524 | + m11 = Tx.m11; | |
525 | + m12 = Tx.m12; | |
526 | + m20 = Tx.m20; | |
527 | + m21 = Tx.m21; | |
528 | + m22 = Tx.m22; | |
529 | + } | |
530 | + | |
531 | + public void setTransform(float m00, float m10, float m20, | |
532 | + float m01, float m11, float m21, | |
533 | + float m02, float m12, float m22) { | |
534 | + this.m00 = (double)m00; | |
535 | + this.m01 = (double)m01; | |
536 | + this.m02 = (double)m02; | |
537 | + this.m10 = (double)m10; | |
538 | + this.m11 = (double)m11; | |
539 | + this.m12 = (double)m12; | |
540 | + this.m20 = (double)m20; | |
541 | + this.m21 = (double)m21; | |
542 | + this.m22 = (double)m22; | |
543 | + } | |
544 | + | |
545 | + public void setTransform(double[][] matrix) { | |
546 | + if ( matrix == null ) { | |
547 | + throw new IllegalArgumentException(JaiI18N.getString("Generic0")); | |
548 | + } | |
549 | + | |
550 | + m00 = matrix[0][0]; | |
551 | + m01 = matrix[0][1]; | |
552 | + m02 = matrix[0][2]; | |
553 | + m10 = matrix[1][0]; | |
554 | + m11 = matrix[1][1]; | |
555 | + m12 = matrix[1][2]; | |
556 | + m20 = matrix[2][0]; | |
557 | + m21 = matrix[2][1]; | |
558 | + m22 = matrix[2][2]; | |
559 | + } | |
560 | + | |
561 | + /** | |
562 | + * Post-concatenates a given AffineTransform to this transform. | |
563 | + * @throws IllegalArgumentException if Tx is null | |
564 | + */ | |
565 | + public void concatenate(AffineTransform Tx) { | |
566 | + if ( Tx == null ) { | |
567 | + throw new IllegalArgumentException(JaiI18N.getString("Generic0")); | |
568 | + } | |
569 | + | |
570 | + double tx_m00 = Tx.getScaleX(); | |
571 | + double tx_m01 = Tx.getShearX(); | |
572 | + double tx_m02 = Tx.getTranslateX(); | |
573 | + double tx_m10 = Tx.getShearY(); | |
574 | + double tx_m11 = Tx.getScaleY(); | |
575 | + double tx_m12 = Tx.getTranslateY(); | |
576 | + | |
577 | + double m00p = m00*tx_m00 + m10*tx_m01 + m20*tx_m02; | |
578 | + double m01p = m01*tx_m00 + m11*tx_m01 + m21*tx_m02; | |
579 | + double m02p = m02*tx_m00 + m12*tx_m01 + m22*tx_m02; | |
580 | + double m10p = m00*tx_m10 + m10*tx_m11 + m20*tx_m12; | |
581 | + double m11p = m01*tx_m10 + m11*tx_m11 + m21*tx_m12; | |
582 | + double m12p = m02*tx_m10 + m12*tx_m11 + m22*tx_m12; | |
583 | + double m20p = m20; | |
584 | + double m21p = m21; | |
585 | + double m22p = m22; | |
586 | + | |
587 | + m00 = m00p; | |
588 | + m10 = m10p; | |
589 | + m20 = m20p; | |
590 | + m01 = m01p; | |
591 | + m11 = m11p; | |
592 | + m21 = m21p; | |
593 | + m02 = m02p; | |
594 | + m12 = m12p; | |
595 | + m22 = m22p; | |
596 | + } | |
597 | + | |
598 | + /** | |
599 | + * Post-concatenates a given PerspectiveTransform to this transform. | |
600 | + * @throws IllegalArgumentException if Tx is null | |
601 | + */ | |
602 | + public void concatenate(PerspectiveTransform Tx) { | |
603 | + if ( Tx == null ) { | |
604 | + throw new IllegalArgumentException(JaiI18N.getString("Generic0")); | |
605 | + } | |
606 | + | |
607 | + double m00p = m00*Tx.m00 + m10*Tx.m01 + m20*Tx.m02; | |
608 | + double m10p = m00*Tx.m10 + m10*Tx.m11 + m20*Tx.m12; | |
609 | + double m20p = m00*Tx.m20 + m10*Tx.m21 + m20*Tx.m22; | |
610 | + double m01p = m01*Tx.m00 + m11*Tx.m01 + m21*Tx.m02; | |
611 | + double m11p = m01*Tx.m10 + m11*Tx.m11 + m21*Tx.m12; | |
612 | + double m21p = m01*Tx.m20 + m11*Tx.m21 + m21*Tx.m22; | |
613 | + double m02p = m02*Tx.m00 + m12*Tx.m01 + m22*Tx.m02; | |
614 | + double m12p = m02*Tx.m10 + m12*Tx.m11 + m22*Tx.m12; | |
615 | + double m22p = m02*Tx.m20 + m12*Tx.m21 + m22*Tx.m22; | |
616 | + | |
617 | + m00 = m00p; | |
618 | + m10 = m10p; | |
619 | + m20 = m20p; | |
620 | + m01 = m01p; | |
621 | + m11 = m11p; | |
622 | + m21 = m21p; | |
623 | + m02 = m02p; | |
624 | + m12 = m12p; | |
625 | + m22 = m22p; | |
626 | + } | |
627 | + | |
628 | + /** | |
629 | + * Pre-concatenates a given AffineTransform to this transform. | |
630 | + * @throws IllegalArgumentException if Tx is null | |
631 | + */ | |
632 | + public void preConcatenate(AffineTransform Tx) { | |
633 | + if ( Tx == null ) { | |
634 | + throw new IllegalArgumentException(JaiI18N.getString("Generic0")); | |
635 | + } | |
636 | + | |
637 | + // Extend Tx: Tx.m20 = 0, Tx.m21 = 0, Tx.m22 = 1 | |
638 | + | |
639 | + double tx_m00 = Tx.getScaleX(); | |
640 | + double tx_m01 = Tx.getShearX(); | |
641 | + double tx_m02 = Tx.getTranslateX(); | |
642 | + double tx_m10 = Tx.getShearY(); | |
643 | + double tx_m11 = Tx.getScaleY(); | |
644 | + double tx_m12 = Tx.getTranslateY(); | |
645 | + | |
646 | + double m00p = tx_m00*m00 + tx_m10*m01; | |
647 | + double m01p = tx_m01*m00 + tx_m11*m01; | |
648 | + double m02p = tx_m02*m00 + tx_m12*m01 + m02; | |
649 | + double m10p = tx_m00*m10 + tx_m10*m11; | |
650 | + double m11p = tx_m01*m10 + tx_m11*m11; | |
651 | + double m12p = tx_m02*m10 + tx_m12*m11 + m12; | |
652 | + double m20p = tx_m00*m20 + tx_m10*m21; | |
653 | + double m21p = tx_m01*m20 + tx_m11*m21; | |
654 | + double m22p = tx_m02*m20 + tx_m12*m21 + m22; | |
655 | + | |
656 | + m00 = m00p; | |
657 | + m10 = m10p; | |
658 | + m20 = m20p; | |
659 | + m01 = m01p; | |
660 | + m11 = m11p; | |
661 | + m21 = m21p; | |
662 | + m02 = m02p; | |
663 | + m12 = m12p; | |
664 | + m22 = m22p; | |
665 | + } | |
666 | + | |
667 | + /** | |
668 | + * Pre-concatenates a given PerspectiveTransform to this transform. | |
669 | + * @throws IllegalArgumentException if Tx is null | |
670 | + */ | |
671 | + public void preConcatenate(PerspectiveTransform Tx) { | |
672 | + if ( Tx == null ) { | |
673 | + throw new IllegalArgumentException(JaiI18N.getString("Generic0")); | |
674 | + } | |
675 | + | |
676 | + double m00p = Tx.m00*m00 + Tx.m10*m01 + Tx.m20*m02; | |
677 | + double m10p = Tx.m00*m10 + Tx.m10*m11 + Tx.m20*m12; | |
678 | + double m20p = Tx.m00*m20 + Tx.m10*m21 + Tx.m20*m22; | |
679 | + double m01p = Tx.m01*m00 + Tx.m11*m01 + Tx.m21*m02; | |
680 | + double m11p = Tx.m01*m10 + Tx.m11*m11 + Tx.m21*m12; | |
681 | + double m21p = Tx.m01*m20 + Tx.m11*m21 + Tx.m21*m22; | |
682 | + double m02p = Tx.m02*m00 + Tx.m12*m01 + Tx.m22*m02; | |
683 | + double m12p = Tx.m02*m10 + Tx.m12*m11 + Tx.m22*m12; | |
684 | + double m22p = Tx.m02*m20 + Tx.m12*m21 + Tx.m22*m22; | |
685 | + | |
686 | + m00 = m00p; | |
687 | + m10 = m10p; | |
688 | + m20 = m20p; | |
689 | + m01 = m01p; | |
690 | + m11 = m11p; | |
691 | + m21 = m21p; | |
692 | + m02 = m02p; | |
693 | + m12 = m12p; | |
694 | + m22 = m22p; | |
695 | + } | |
696 | + | |
697 | + /** | |
698 | + * Returns a new PerpectiveTransform that is the inverse | |
699 | + * of the current transform. | |
700 | + * @throws NoninvertibleTransformException if transform cannot be inverted | |
701 | + */ | |
702 | + public PerspectiveTransform createInverse() | |
703 | + throws NoninvertibleTransformException, CloneNotSupportedException { | |
704 | + | |
705 | + PerspectiveTransform tx = (PerspectiveTransform)clone(); | |
706 | + tx.makeAdjoint(); | |
707 | + if (Math.abs(tx.m22) < PERSPECTIVE_DIVIDE_EPSILON) { | |
708 | + throw new NoninvertibleTransformException(JaiI18N.getString("PerspectiveTransform0")); | |
709 | + } | |
710 | + tx.normalize(); | |
711 | + return tx; | |
712 | + } | |
713 | + | |
714 | + /** | |
715 | + * Returns a new PerpectiveTransform that is the adjoint, | |
716 | + * of the current transform. The adjoint is defined as | |
717 | + * the matrix of cofactors, which in turn are the determinants | |
718 | + * of the submatrices defined by removing the row and column | |
719 | + * of each element from the original matrix in turn. | |
720 | + */ | |
721 | + public PerspectiveTransform createAdjoint() | |
722 | + throws CloneNotSupportedException{ | |
723 | + | |
724 | + PerspectiveTransform tx = (PerspectiveTransform)clone(); | |
725 | + tx.makeAdjoint(); | |
726 | + return tx; | |
727 | + } | |
728 | + | |
729 | + /** | |
730 | + * Transforms the specified ptSrc and stores the result in ptDst. | |
731 | + * If ptDst is null, a new Point2D object will be allocated before | |
732 | + * storing. In either case, ptDst containing the transformed point | |
733 | + * is returned for convenience. | |
734 | + * Note that ptSrc and ptDst can the same. In this case, the input | |
735 | + * point will be overwritten with the transformed point. | |
736 | + * @throws IllegalArgumentException if ptSrc is null | |
737 | + */ | |
738 | + public Point2D transform(Point2D ptSrc, Point2D ptDst) { | |
739 | + if ( ptSrc == null ) { | |
740 | + throw new IllegalArgumentException(JaiI18N.getString("Generic0")); | |
741 | + } | |
742 | + | |
743 | + if (ptDst == null) { | |
744 | + if (ptSrc instanceof Point2D.Double) { | |
745 | + ptDst = new Point2D.Double(); | |
746 | + } else { | |
747 | + ptDst = new Point2D.Float(); | |
748 | + } | |
749 | + } | |
750 | + | |
751 | + double x = ptSrc.getX(); | |
752 | + double y = ptSrc.getY(); | |
753 | + double w = m20 * x + m21 * y + m22; | |
754 | + ptDst.setLocation((m00 * x + m01 * y + m02) / w, | |
755 | + (m10 * x + m11 * y + m12) / w); | |
756 | + | |
757 | + return ptDst; | |
758 | + } | |
759 | + | |
760 | + /** | |
761 | + * Transforms an array of point objects by this transform. | |
762 | + * @throws IllegalArgumentException if ptSrc is null | |
763 | + * @throws IllegalArgumentException if ptDst is null | |
764 | + * @throws ArrayIndexOutOfBoundsException if ptSrc is too small | |
765 | + */ | |
766 | + public void transform(Point2D[] ptSrc, int srcOff, | |
767 | + Point2D[] ptDst, int dstOff, | |
768 | + int numPts) { | |
769 | + | |
770 | + if ( ptSrc == null || ptDst == null ) { | |
771 | + throw new IllegalArgumentException(JaiI18N.getString("Generic0")); | |
772 | + } | |
773 | + | |
774 | + while (numPts-- > 0) { | |
775 | + Point2D src = ptSrc[srcOff++]; | |
776 | + Point2D dst = ptDst[dstOff++]; | |
777 | + if (dst == null) { | |
778 | + if (src instanceof Point2D.Double) { | |
779 | + dst = new Point2D.Double(); | |
780 | + } else { | |
781 | + dst = new Point2D.Float(); | |
782 | + } | |
783 | + ptDst[dstOff - 1] = dst; | |
784 | + } | |
785 | + | |
786 | + double x = src.getX(); | |
787 | + double y = src.getY(); | |
788 | + double w = m20 * x + m21 * y + m22; | |
789 | + | |
790 | + if (w == 0) { | |
791 | + dst.setLocation(x, y); | |
792 | + } else { | |
793 | + dst.setLocation((m00 * x + m01 * y + m02) / w, | |
794 | + (m10 * x + m11 * y + m12) / w); | |
795 | + } | |
796 | + } | |
797 | + } | |
798 | + | |
799 | + /** | |
800 | + * Transforms an array of floating point coordinates by this transform. | |
801 | + * @throws IllegalArgumentException if srcPts is null | |
802 | + * @throws ArrayIndexOutOfBoundsException if srcPts is too small | |
803 | + */ | |
804 | + public void transform(float[] srcPts, int srcOff, | |
805 | + float[] dstPts, int dstOff, | |
806 | + int numPts) { | |
807 | + | |
808 | + if ( srcPts == null ) { | |
809 | + throw new IllegalArgumentException(JaiI18N.getString("Generic0")); | |
810 | + } | |
811 | + | |
812 | + if (dstPts == null) { | |
813 | + dstPts = new float[numPts * 2 + dstOff]; | |
814 | + } | |
815 | + | |
816 | + while (numPts-- > 0) { | |
817 | + float x = srcPts[srcOff++]; | |
818 | + float y = srcPts[srcOff++]; | |
819 | + double w = m20 * x + m21 * y + m22; | |
820 | + | |
821 | + if (w == 0) { | |
822 | + dstPts[dstOff++] = x; | |
823 | + dstPts[dstOff++] = y; | |
824 | + } else { | |
825 | + dstPts[dstOff++] = (float)((m00 * x + m01 * y + m02) / w); | |
826 | + dstPts[dstOff++] = (float)((m10 * x + m11 * y + m12) / w); | |
827 | + } | |
828 | + } | |
829 | + } | |
830 | + | |
831 | + /** | |
832 | + * Transforms an array of double precision coordinates by this transform. | |
833 | + * @throws IllegalArgumentException if srcPts is null | |
834 | + * @throws ArrayIndexOutOfBoundsException if srcPts is too small | |
835 | + */ | |
836 | + public void transform(double[] srcPts, int srcOff, | |
837 | + double[] dstPts, int dstOff, | |
838 | + int numPts) { | |
839 | + | |
840 | + if ( srcPts == null ) { | |
841 | + throw new IllegalArgumentException(JaiI18N.getString("Generic0")); | |
842 | + } | |
843 | + | |
844 | + if (dstPts == null) { | |
845 | + dstPts = new double[numPts * 2 + dstOff]; | |
846 | + } | |
847 | + | |
848 | + while (numPts-- > 0) { | |
849 | + double x = srcPts[srcOff++]; | |
850 | + double y = srcPts[srcOff++]; | |
851 | + double w = m20 * x + m21 * y + m22; | |
852 | + | |
853 | + if (w == 0) { | |
854 | + dstPts[dstOff++] = x; | |
855 | + dstPts[dstOff++] = y; | |
856 | + } else { | |
857 | + dstPts[dstOff++] = (m00 * x + m01 * y + m02) / w; | |
858 | + dstPts[dstOff++] = (m10 * x + m11 * y + m12) / w; | |
859 | + } | |
860 | + } | |
861 | + } | |
862 | + | |
863 | + /** | |
864 | + * Transforms an array of floating point coordinates by this transform, | |
865 | + * storing the results into an array of doubles. | |
866 | + * @throws IllegalArgumentException if srcPts is null | |
867 | + * @throws ArrayIndexOutOfBoundsException if srcPts is too small | |
868 | + */ | |
869 | + public void transform(float[] srcPts, int srcOff, | |
870 | + double[] dstPts, int dstOff, | |
871 | + int numPts) { | |
872 | + | |
873 | + if ( srcPts == null ) { | |
874 | + throw new IllegalArgumentException(JaiI18N.getString("Generic0")); | |
875 | + } | |
876 | + | |
877 | + if (dstPts == null) { | |
878 | + dstPts = new double[numPts * 2 + dstOff]; | |
879 | + } | |
880 | + | |
881 | + while (numPts-- > 0) { | |
882 | + float x = srcPts[srcOff++]; | |
883 | + float y = srcPts[srcOff++]; | |
884 | + double w = m20 * x + m21 * y + m22; | |
885 | + | |
886 | + if (w == 0) { | |
887 | + dstPts[dstOff++] = x; | |
888 | + dstPts[dstOff++] = y; | |
889 | + } else { | |
890 | + dstPts[dstOff++] = (m00 * x + m01 * y + m02) / w; | |
891 | + dstPts[dstOff++] = (m10 * x + m11 * y + m12) / w; | |
892 | + } | |
893 | + } | |
894 | + } | |
895 | + | |
896 | + /** | |
897 | + * Transforms an array of double precision coordinates by this transform, | |
898 | + * storing the results into an array of floats. | |
899 | + * @throws IllegalArgumentException if srcPts is null | |
900 | + * @throws ArrayIndexOutOfBoundsException if srcPts is too small | |
901 | + */ | |
902 | + public void transform(double[] srcPts, int srcOff, | |
903 | + float[] dstPts, int dstOff, | |
904 | + int numPts) { | |
905 | + | |
906 | + if ( srcPts == null ) { | |
907 | + throw new IllegalArgumentException(JaiI18N.getString("Generic0")); | |
908 | + } | |
909 | + | |
910 | + if (dstPts == null) { | |
911 | + dstPts = new float[numPts * 2 + dstOff]; | |
912 | + } | |
913 | + | |
914 | + while (numPts-- > 0) { | |
915 | + double x = srcPts[srcOff++]; | |
916 | + double y = srcPts[srcOff++]; | |
917 | + double w = m20 * x + m21 * y + m22; | |
918 | + | |
919 | + if (w == 0) { | |
920 | + dstPts[dstOff++] = (float)x; | |
921 | + dstPts[dstOff++] = (float)y; | |
922 | + } else { | |
923 | + dstPts[dstOff++] = (float)((m00 * x + m01 * y + m02) / w); | |
924 | + dstPts[dstOff++] = (float)((m10 * x + m11 * y + m12) / w); | |
925 | + } | |
926 | + } | |
927 | + } | |
928 | + | |
929 | + /** | |
930 | + * Inverse transforms the specified ptSrc and stores the result in ptDst. | |
931 | + * If ptDst is null, a new Point2D object will be allocated before | |
932 | + * storing. In either case, ptDst containing the transformed point | |
933 | + * is returned for convenience. | |
934 | + | |
935 | + * @throws NoninvertibleTransformException if the matrix cannot be inverted. | |
936 | + * @throws IllegalArgumentException if ptSrc is null | |
937 | + */ | |
938 | + public Point2D inverseTransform(Point2D ptSrc, Point2D ptDst) | |
939 | + throws NoninvertibleTransformException | |
940 | + { | |
941 | + if ( ptSrc == null ) { | |
942 | + throw new IllegalArgumentException(JaiI18N.getString("Generic0")); | |
943 | + } | |
944 | + | |
945 | + if (ptDst == null) { | |
946 | + if (ptSrc instanceof Point2D.Double) { | |
947 | + ptDst = new Point2D.Double(); | |
948 | + } else { | |
949 | + ptDst = new Point2D.Float(); | |
950 | + } | |
951 | + } | |
952 | + // Copy source coords into local variables in case src == dst | |
953 | + double x = ptSrc.getX(); | |
954 | + double y = ptSrc.getY(); | |
955 | + | |
956 | + double tmp_x = (m11*m22 - m12*m21) * x + | |
957 | + (m02*m21 - m01*m22) * y + | |
958 | + (m01*m12 - m02*m11); | |
959 | + double tmp_y = (m12*m20 - m10*m22) * x + | |
960 | + (m00*m22 - m02*m20) * y + | |
961 | + (m02*m10 - m00*m12); | |
962 | + double w = (m10*m21 - m11*m20) * x + | |
963 | + (m01*m20 - m00*m21) * y + | |
964 | + (m00*m11 - m01*m10); | |
965 | + | |
966 | + double wabs = w; | |
967 | + if (w < 0) { | |
968 | + wabs = - w; | |
969 | + } | |
970 | + if (wabs < PERSPECTIVE_DIVIDE_EPSILON) { | |
971 | + throw new | |
972 | + NoninvertibleTransformException( | |
973 | + JaiI18N.getString("PerspectiveTransform1")); | |
974 | + } | |
975 | + | |
976 | + ptDst.setLocation(tmp_x/w, tmp_y/w); | |
977 | + | |
978 | + return ptDst; | |
979 | + } | |
980 | + | |
981 | + /** | |
982 | + * Inverse transforms an array of double precision coordinates by | |
983 | + * this transform. | |
984 | + * @throws NoninvertibleTransformException if the matrix cannot be inverted. | |
985 | + * @throws IllegalArgumentException if srcPts is null | |
986 | + * @throws ArrayIndexOutOfBoundsException if srcPts is too small | |
987 | + * @throws NoninvertibleTransformException transform cannot be inverted | |
988 | + */ | |
989 | + public void inverseTransform(double[] srcPts, int srcOff, | |
990 | + double[] dstPts, int dstOff, | |
991 | + int numPts) | |
992 | + throws NoninvertibleTransformException | |
993 | + { | |
994 | + if ( srcPts == null ) { | |
995 | + throw new IllegalArgumentException(JaiI18N.getString("Generic0")); | |
996 | + } | |
997 | + | |
998 | + if (dstPts == null) { | |
999 | + dstPts = new double[numPts * 2 + dstOff]; | |
1000 | + } | |
1001 | + | |
1002 | + while (numPts-- > 0) { | |
1003 | + double x = srcPts[srcOff++]; | |
1004 | + double y = srcPts[srcOff++]; | |
1005 | + | |
1006 | + double tmp_x = (m11*m22 - m12*m21) * x + | |
1007 | + (m02*m21 - m01*m22) * y + | |
1008 | + (m01*m12 - m02*m11); | |
1009 | + double tmp_y = (m12*m20 - m10*m22) * x + | |
1010 | + (m00*m22 - m02*m20) * y + | |
1011 | + (m02*m10 - m00*m12); | |
1012 | + double w = (m10*m21 - m11*m20) * x + | |
1013 | + (m01*m20 - m00*m21) * y + | |
1014 | + (m00*m11 - m01*m10); | |
1015 | + | |
1016 | + double wabs = w; | |
1017 | + if (w < 0) { | |
1018 | + wabs = - w; | |
1019 | + } | |
1020 | + if (wabs < PERSPECTIVE_DIVIDE_EPSILON) { | |
1021 | + throw new NoninvertibleTransformException( | |
1022 | + JaiI18N.getString("PerspectiveTransform1")); | |
1023 | + } | |
1024 | + | |
1025 | + dstPts[dstOff++] = tmp_x / w; | |
1026 | + dstPts[dstOff++] = tmp_y / w; | |
1027 | + } | |
1028 | + } | |
1029 | + | |
1030 | + /** | |
1031 | + * Returns a String that represents the value of this Object. | |
1032 | + */ | |
1033 | + public String toString() { | |
1034 | + StringBuffer sb = new StringBuffer(); | |
1035 | + sb.append("Perspective transform matrix\n"); | |
1036 | + sb.append(this.m00); | |
1037 | + sb.append("\t"); | |
1038 | + sb.append(this.m01); | |
1039 | + sb.append("\t"); | |
1040 | + sb.append(this.m02); | |
1041 | + sb.append("\n"); | |
1042 | + sb.append(this.m10); | |
1043 | + sb.append("\t"); | |
1044 | + sb.append(this.m11); | |
1045 | + sb.append("\t"); | |
1046 | + sb.append(this.m12); | |
1047 | + sb.append("\n"); | |
1048 | + sb.append(this.m20); | |
1049 | + sb.append("\t"); | |
1050 | + sb.append(this.m21); | |
1051 | + sb.append("\t"); | |
1052 | + sb.append(this.m22); | |
1053 | + sb.append("\n"); | |
1054 | + return new String(sb); | |
1055 | + } | |
1056 | + | |
1057 | + /** | |
1058 | + * Returns the boolean true value if this PerspectiveTransform is an | |
1059 | + * identity transform. Returns false otherwise. | |
1060 | + */ | |
1061 | + public boolean isIdentity() { | |
1062 | + return m01 == 0.0 && m02 == 0.0 && | |
1063 | + m10 == 0.0 && m12 == 0.0 && | |
1064 | + m20 == 0.0 && m21 == 0.0 && | |
1065 | + m22 != 0.0 && m00/m22 == 1.0 && m11/m22 == 1.0; | |
1066 | + } | |
1067 | + | |
1068 | + /** | |
1069 | + * Returns a copy of this PerspectiveTransform object. | |
1070 | + */ | |
1071 | + public Object clone() { | |
1072 | + try { | |
1073 | + return super.clone(); | |
1074 | + } catch (CloneNotSupportedException e) { | |
1075 | + // this shouldn't happen, since we are Cloneable | |
1076 | + throw new InternalError(); | |
1077 | + } | |
1078 | + } | |
1079 | + | |
1080 | + | |
1081 | + /** | |
1082 | + * Tests if this PerspectiveTransform equals a supplied one. | |
1083 | + */ | |
1084 | + public boolean equals(Object obj) { | |
1085 | + if (!(obj instanceof PerspectiveTransform)) { | |
1086 | + return false; | |
1087 | + } | |
1088 | + | |
1089 | + PerspectiveTransform a = (PerspectiveTransform)obj; | |
1090 | + | |
1091 | + return ((m00 == a.m00) && (m10 == a.m10) && (m20 == a.m20) && | |
1092 | + (m01 == a.m01) && (m11 == a.m11) && (m21 == a.m21) && | |
1093 | + (m02 == a.m02) && (m12 == a.m12) && (m22 == a.m22)); | |
1094 | + } | |
1095 | +} | ... | ... |
WiimoteWhiteboard/src/javax/media/jai/PerspectiveTransform.java~
0 → 100644
... | ... | @@ -0,0 +1,1439 @@ |
1 | +/* | |
2 | + * $RCSfile: PerspectiveTransform.java,v $ | |
3 | + * | |
4 | + * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. | |
5 | + * | |
6 | + * Use is subject to license terms. | |
7 | + * | |
8 | + * $Revision: 1.1 $ | |
9 | + * $Date: 2005/02/11 04:57:15 $ | |
10 | + * $State: Exp $ | |
11 | + */ | |
12 | +package javax.media.jai; | |
13 | +import java.awt.geom.AffineTransform; | |
14 | +import java.awt.geom.Point2D; | |
15 | +import java.awt.geom.NoninvertibleTransformException; | |
16 | +import java.io.Serializable; | |
17 | + | |
18 | + | |
19 | +/** | |
20 | + * A 2D perspective (or projective) transform, used by various OpImages. | |
21 | + * | |
22 | + * <p> A perspective transformation is capable of mapping an arbitrary | |
23 | + * quadrilateral into another arbitrary quadrilateral, while | |
24 | + * preserving the straightness of lines. Unlike an affine | |
25 | + * transformation, the parallelism of lines in the source is not | |
26 | + * necessarily preserved in the output. | |
27 | + * | |
28 | + * <p> Such a coordinate transformation can be represented by a 3x3 | |
29 | + * matrix which transforms homogenous source coordinates | |
30 | + * <code>(x, y, 1)</code> into destination coordinates | |
31 | + * <code>(x', y', w)</code>. To convert back into non-homogenous | |
32 | + * coordinates (X, Y), <code>x'</code> and <code>y'</code> are divided by | |
33 | + * <code>w</code>. | |
34 | + * | |
35 | + * <pre> | |
36 | + * [ x'] [ m00 m01 m02 ] [ x ] [ m00x + m01y + m02 ] | |
37 | + * [ y'] = [ m10 m11 m12 ] [ y ] = [ m10x + m11y + m12 ] | |
38 | + * [ w ] [ m20 m21 m22 ] [ 1 ] [ m20x + m21y + m22 ] | |
39 | + * | |
40 | + * x' = (m00x + m01y + m02) | |
41 | + * y' = (m10x + m11y + m12) | |
42 | + * | |
43 | + * w = (m20x + m21y + m22) | |
44 | + * | |
45 | + * X = x' / w | |
46 | + * Y = y' / w | |
47 | + * </pre> | |
48 | + */ | |
49 | +public final class PerspectiveTransform implements Cloneable, Serializable { | |
50 | + | |
51 | + private static final double PERSPECTIVE_DIVIDE_EPSILON = 1.0e-10; | |
52 | + | |
53 | + /** An element of the transform matrix. */ | |
54 | + double m00, m01, m02, m10, m11, m12, m20, m21, m22; | |
55 | + | |
56 | + /** Constructs an identity PerspectiveTransform. */ | |
57 | + public PerspectiveTransform() { | |
58 | + m00 = m11 = m22 = 1.0; | |
59 | + m01 = m02 = m10 = m12 = m20 = m21 = 0.0; | |
60 | + } | |
61 | + | |
62 | + /** | |
63 | + * Constructs a new PerspectiveTransform from 9 floats. | |
64 | + * @deprecated as of JAI 1.1 Use PerspectiveTransform(double[][]) instead. | |
65 | + */ | |
66 | + public PerspectiveTransform(float m00, float m01, float m02, | |
67 | + float m10, float m11, float m12, | |
68 | + float m20, float m21, float m22) { | |
69 | + this.m00 = m00; | |
70 | + this.m01 = m01; | |
71 | + this.m02 = m02; | |
72 | + this.m10 = m10; | |
73 | + this.m11 = m11; | |
74 | + this.m12 = m12; | |
75 | + this.m20 = m20; | |
76 | + this.m21 = m21; | |
77 | + this.m22 = m22; | |
78 | + } | |
79 | + | |
80 | + /** | |
81 | + * Constructs a new PerspectiveTransform from 9 doubles. | |
82 | + * @deprecated as of JAI 1.1 Use PerspectiveTransform(double[][]) instead. | |
83 | + */ | |
84 | + public PerspectiveTransform(double m00, double m01, double m02, | |
85 | + double m10, double m11, double m12, | |
86 | + double m20, double m21, double m22) { | |
87 | + this.m00 = m00; | |
88 | + this.m01 = m01; | |
89 | + this.m02 = m02; | |
90 | + this.m10 = m10; | |
91 | + this.m11 = m11; | |
92 | + this.m12 = m12; | |
93 | + this.m20 = m20; | |
94 | + this.m21 = m21; | |
95 | + this.m22 = m22; | |
96 | + } | |
97 | + | |
98 | + /** | |
99 | + * Constructs a new PerspectiveTransform from a one-dimensional | |
100 | + * array of 9 floats, in row-major order. | |
101 | + * The values in the array are assumed to be | |
102 | + * { m00 m01 m02 m10 m11 m12 m20 m21 m22 }. | |
103 | + * @throws IllegalArgumentException if flatmatrix is null | |
104 | + * @throws ArrayIndexOutOfBoundsException if flatmatrix is too small | |
105 | + * @deprecated as of JAI 1.1 Use PerspectiveTransform(double[][]) instead. | |
106 | + */ | |
107 | + public PerspectiveTransform(float[] flatmatrix) { | |
108 | + if ( flatmatrix == null ) { | |
109 | + throw new IllegalArgumentException(JaiI18N.getString("Generic0")); | |
110 | + } | |
111 | + | |
112 | + m00 = flatmatrix[0]; | |
113 | + m01 = flatmatrix[1]; | |
114 | + m02 = flatmatrix[2]; | |
115 | + m10 = flatmatrix[3]; | |
116 | + m11 = flatmatrix[4]; | |
117 | + m12 = flatmatrix[5]; | |
118 | + m20 = flatmatrix[6]; | |
119 | + m21 = flatmatrix[7]; | |
120 | + m22 = flatmatrix[8]; | |
121 | + } | |
122 | + | |
123 | + /** | |
124 | + * Constructs a new PerspectiveTransform from a two-dimensional | |
125 | + * array of floats. | |
126 | + * @throws IllegalArgumentException if matrix is null | |
127 | + * @throws ArrayIndexOutOfBoundsException if matrix is too small | |
128 | + * | |
129 | + * @deprecated as of JAI 1.1 Use PerspectiveTransform(double[][]) instead. | |
130 | + */ | |
131 | + public PerspectiveTransform(float[][] matrix) { | |
132 | + if ( matrix == null ) { | |
133 | + throw new IllegalArgumentException(JaiI18N.getString("Generic0")); | |
134 | + } | |
135 | + | |
136 | + m00 = matrix[0][0]; | |
137 | + m01 = matrix[0][1]; | |
138 | + m02 = matrix[0][2]; | |
139 | + m10 = matrix[1][0]; | |
140 | + m11 = matrix[1][1]; | |
141 | + m12 = matrix[1][2]; | |
142 | + m20 = matrix[2][0]; | |
143 | + m21 = matrix[2][1]; | |
144 | + m22 = matrix[2][2]; | |
145 | + } | |
146 | + | |
147 | + /** | |
148 | + * Constructs a new PerspectiveTransform from a one-dimensional | |
149 | + * array of 9 doubles, in row-major order. | |
150 | + * The values in the array are assumed to be | |
151 | + * { m00 m01 m02 m10 m11 m12 m20 m21 m22 }. | |
152 | + * @throws IllegalArgumentException if flatmatrix is null | |
153 | + * @throws ArrayIndexOutOfBoundsException if flatmatrix is too small | |
154 | + * | |
155 | + * @deprecated as of JAI 1.1 Use PerspectiveTransform(double[][]) instead. | |
156 | + */ | |
157 | + public PerspectiveTransform(double[] flatmatrix) { | |
158 | + if ( flatmatrix == null ) { | |
159 | + throw new IllegalArgumentException(JaiI18N.getString("Generic0")); | |
160 | + } | |
161 | + | |
162 | + m00 = flatmatrix[0]; | |
163 | + m01 = flatmatrix[1]; | |
164 | + m02 = flatmatrix[2]; | |
165 | + m10 = flatmatrix[3]; | |
166 | + m11 = flatmatrix[4]; | |
167 | + m12 = flatmatrix[5]; | |
168 | + m20 = flatmatrix[6]; | |
169 | + m21 = flatmatrix[7]; | |
170 | + m22 = flatmatrix[8]; | |
171 | + } | |
172 | + | |
173 | + /** | |
174 | + * Constructs a new PerspectiveTransform from a two-dimensional | |
175 | + * array of doubles. | |
176 | + * @throws IllegalArgumentException if matrix is null | |
177 | + * @throws ArrayIndexOutOfBoundsException if matrix is too small | |
178 | + */ | |
179 | + public PerspectiveTransform(double[][] matrix) { | |
180 | + if ( matrix == null ) { | |
181 | + throw new IllegalArgumentException(JaiI18N.getString("Generic0")); | |
182 | + } | |
183 | + | |
184 | + m00 = matrix[0][0]; | |
185 | + m01 = matrix[0][1]; | |
186 | + m02 = matrix[0][2]; | |
187 | + m10 = matrix[1][0]; | |
188 | + m11 = matrix[1][1]; | |
189 | + m12 = matrix[1][2]; | |
190 | + m20 = matrix[2][0]; | |
191 | + m21 = matrix[2][1]; | |
192 | + m22 = matrix[2][2]; | |
193 | + } | |
194 | + | |
195 | + /** | |
196 | + * Constructs a new PerspectiveTransform with the same effect | |
197 | + * as an existing AffineTransform. | |
198 | + * @throws IllegalArgumentException if transform is null | |
199 | + */ | |
200 | + public PerspectiveTransform(AffineTransform transform) { | |
201 | + if ( transform == null ) { | |
202 | + throw new IllegalArgumentException(JaiI18N.getString("Generic0")); | |
203 | + } | |
204 | + | |
205 | + m00 = transform.getScaleX(); | |
206 | + m01 = transform.getShearX(); | |
207 | + m02 = transform.getTranslateX(); | |
208 | + m10 = transform.getShearY(); | |
209 | + m11 = transform.getScaleY(); | |
210 | + m12 = transform.getTranslateY(); | |
211 | + m20 = 0.0; | |
212 | + m21 = 0.0; | |
213 | + m22 = 1.0; | |
214 | + } | |
215 | + | |
216 | + /** | |
217 | + * Replaces the matrix with its adjoint. | |
218 | + */ | |
219 | + private final void makeAdjoint() { | |
220 | + double m00p = m11*m22 - m12*m21; | |
221 | + double m01p = m12*m20 - m10*m22; // flipped sign | |
222 | + double m02p = m10*m21 - m11*m20; | |
223 | + double m10p = m02*m21 - m01*m22; // flipped sign | |
224 | + double m11p = m00*m22 - m02*m20; | |
225 | + double m12p = m01*m20 - m00*m21; // flipped sign | |
226 | + double m20p = m01*m12 - m02*m11; | |
227 | + double m21p = m02*m10 - m00*m12; // flipped sign | |
228 | + double m22p = m00*m11 - m01*m10; | |
229 | + | |
230 | + // Transpose and copy sub-determinants | |
231 | + m00 = m00p; | |
232 | + m01 = m10p; | |
233 | + m02 = m20p; | |
234 | + m10 = m01p; | |
235 | + m11 = m11p; | |
236 | + m12 = m21p; | |
237 | + m20 = m02p; | |
238 | + m21 = m12p; | |
239 | + m22 = m22p; | |
240 | + } | |
241 | + | |
242 | + /** | |
243 | + * Scales the matrix elements so m22 is equal to 1.0. | |
244 | + * m22 must not be equal to 0. | |
245 | + */ | |
246 | + private final void normalize() { | |
247 | + double invscale = 1.0/m22; | |
248 | + m00 *= invscale; | |
249 | + m01 *= invscale; | |
250 | + m02 *= invscale; | |
251 | + m10 *= invscale; | |
252 | + m11 *= invscale; | |
253 | + m12 *= invscale; | |
254 | + m20 *= invscale; | |
255 | + m21 *= invscale; | |
256 | + m22 = 1.0; | |
257 | + } | |
258 | + | |
259 | + private static final void getSquareToQuad(double x0, double y0, | |
260 | + double x1, double y1, | |
261 | + double x2, double y2, | |
262 | + double x3, double y3, | |
263 | + PerspectiveTransform tx) { | |
264 | + double dx3 = x0 - x1 + x2 - x3; | |
265 | + double dy3 = y0 - y1 + y2 - y3; | |
266 | + | |
267 | + tx.m22 = 1.0F; | |
268 | + | |
269 | + if ((dx3 == 0.0F) && (dy3 == 0.0F)) { // to do: use tolerance | |
270 | + tx.m00 = x1 - x0; | |
271 | + tx.m01 = x2 - x1; | |
272 | + tx.m02 = x0; | |
273 | + tx.m10 = y1 - y0; | |
274 | + tx.m11 = y2 - y1; | |
275 | + tx.m12 = y0; | |
276 | + tx.m20 = 0.0F; | |
277 | + tx.m21 = 0.0F; | |
278 | + } else { | |
279 | + double dx1 = x1 - x2; | |
280 | + double dy1 = y1 - y2; | |
281 | + double dx2 = x3 - x2; | |
282 | + double dy2 = y3 - y2; | |
283 | + | |
284 | + double invdet = 1.0F/(dx1*dy2 - dx2*dy1); | |
285 | + tx.m20 = (dx3*dy2 - dx2*dy3)*invdet; | |
286 | + tx.m21 = (dx1*dy3 - dx3*dy1)*invdet; | |
287 | + tx.m00 = x1 - x0 + tx.m20*x1; | |
288 | + tx.m01 = x3 - x0 + tx.m21*x3; | |
289 | + tx.m02 = x0; | |
290 | + tx.m10 = y1 - y0 + tx.m20*y1; | |
291 | + tx.m11 = y3 - y0 + tx.m21*y3; | |
292 | + tx.m12 = y0; | |
293 | + } | |
294 | + } | |
295 | + | |
296 | + /** | |
297 | + * Creates a PerspectiveTransform that maps the unit square | |
298 | + * onto an arbitrary quadrilateral. | |
299 | + * | |
300 | + * <pre> | |
301 | + * (0, 0) -> (x0, y0) | |
302 | + * (1, 0) -> (x1, y1) | |
303 | + * (1, 1) -> (x2, y2) | |
304 | + * (0, 1) -> (x3, y3) | |
305 | + * </pre> | |
306 | + */ | |
307 | + public static PerspectiveTransform getSquareToQuad(double x0, double y0, | |
308 | + double x1, double y1, | |
309 | + double x2, double y2, | |
310 | + double x3, double y3) { | |
311 | + PerspectiveTransform tx = new PerspectiveTransform(); | |
312 | + getSquareToQuad(x0, y0, x1, y1, x2, y2, x3, y3, tx); | |
313 | + return tx; | |
314 | + } | |
315 | + | |
316 | + | |
317 | + /** | |
318 | + * Creates a PerspectiveTransform that maps the unit square | |
319 | + * onto an arbitrary quadrilateral. | |
320 | + * | |
321 | + * <pre> | |
322 | + * (0, 0) -> (x0, y0) | |
323 | + * (1, 0) -> (x1, y1) | |
324 | + * (1, 1) -> (x2, y2) | |
325 | + * (0, 1) -> (x3, y3) | |
326 | + * </pre> | |
327 | + */ | |
328 | + public static PerspectiveTransform getSquareToQuad(float x0, float y0, | |
329 | + float x1, float y1, | |
330 | + float x2, float y2, | |
331 | + float x3, float y3) { | |
332 | + return getSquareToQuad((double)x0, (double)y0, | |
333 | + (double)x1, (double)y1, | |
334 | + (double)x2, (double)y2, | |
335 | + (double)x3, (double)y3); | |
336 | + } | |
337 | + | |
338 | + | |
339 | + /** | |
340 | + * Creates a PerspectiveTransform that maps an arbitrary | |
341 | + * quadrilateral onto the unit square. | |
342 | + * | |
343 | + * <pre> | |
344 | + * (x0, y0) -> (0, 0) | |
345 | + * (x1, y1) -> (1, 0) | |
346 | + * (x2, y2) -> (1, 1) | |
347 | + * (x3, y3) -> (0, 1) | |
348 | + * </pre> | |
349 | + */ | |
350 | + public static PerspectiveTransform getQuadToSquare(double x0, double y0, | |
351 | + double x1, double y1, | |
352 | + double x2, double y2, | |
353 | + double x3, double y3) { | |
354 | + PerspectiveTransform tx = new PerspectiveTransform(); | |
355 | + getSquareToQuad(x0, y0, x1, y1, x2, y2, x3, y3, tx); | |
356 | + tx.makeAdjoint(); | |
357 | + return tx; | |
358 | + } | |
359 | + | |
360 | + /** | |
361 | + * Creates a PerspectiveTransform that maps an arbitrary | |
362 | + * quadrilateral onto the unit square. | |
363 | + * | |
364 | + * <pre> | |
365 | + * (x0, y0) -> (0, 0) | |
366 | + * (x1, y1) -> (1, 0) | |
367 | + * (x2, y2) -> (1, 1) | |
368 | + * (x3, y3) -> (0, 1) | |
369 | + * </pre> | |
370 | + */ | |
371 | + public static PerspectiveTransform getQuadToSquare(float x0, float y0, | |
372 | + float x1, float y1, | |
373 | + float x2, float y2, | |
374 | + float x3, float y3) { | |
375 | + return getQuadToSquare((double)x0, (double)y0, | |
376 | + (double)x1, (double)y1, | |
377 | + (double)x2, (double)y2, | |
378 | + (double)x3, (double)y3); | |
379 | + } | |
380 | + | |
381 | + /** | |
382 | + * Creates a PerspectiveTransform that maps an arbitrary | |
383 | + * quadrilateral onto another arbitrary quadrilateral. | |
384 | + * | |
385 | + * <pre> | |
386 | + * (x0, y0) -> (x0p, y0p) | |
387 | + * (x1, y1) -> (x1p, y1p) | |
388 | + * (x2, y2) -> (x2p, y2p) | |
389 | + * (x3, y3) -> (x3p, y3p) | |
390 | + * </pre> | |
391 | + */ | |
392 | + public static PerspectiveTransform getQuadToQuad(double x0, double y0, | |
393 | + double x1, double y1, | |
394 | + double x2, double y2, | |
395 | + double x3, double y3, | |
396 | + double x0p, double y0p, | |
397 | + double x1p, double y1p, | |
398 | + double x2p, double y2p, | |
399 | + double x3p, double y3p) { | |
400 | + PerspectiveTransform tx1 = | |
401 | + getQuadToSquare(x0, y0, x1, y1, x2, y2, x3, y3); | |
402 | + | |
403 | + PerspectiveTransform tx2 = | |
404 | + getSquareToQuad(x0p, y0p, x1p, y1p, x2p, y2p, x3p, y3p); | |
405 | + | |
406 | + tx1.concatenate(tx2); | |
407 | + return tx1; | |
408 | + } | |
409 | + | |
410 | + | |
411 | + /** | |
412 | + * Creates a PerspectiveTransform that maps an arbitrary | |
413 | + * quadrilateral onto another arbitrary quadrilateral. | |
414 | + * | |
415 | + * <pre> | |
416 | + * (x0, y0) -> (x0p, y0p) | |
417 | + * (x1, y1) -> (x1p, y1p) | |
418 | + * (x2, y2) -> (x2p, y2p) | |
419 | + * (x3, y3) -> (x3p, y3p) | |
420 | + * </pre> | |
421 | + */ | |
422 | + public static PerspectiveTransform getQuadToQuad(float x0, float y0, | |
423 | + float x1, float y1, | |
424 | + float x2, float y2, | |
425 | + float x3, float y3, | |
426 | + float x0p, float y0p, | |
427 | + float x1p, float y1p, | |
428 | + float x2p, float y2p, | |
429 | + float x3p, float y3p) { | |
430 | + return getQuadToQuad((double)x0, (double)y0, | |
431 | + (double)x1, (double)y1, | |
432 | + (double)x2, (double)y2, | |
433 | + (double)x3, (double)y3, | |
434 | + (double)x0p, (double)y0p, | |
435 | + (double)x1p, (double)y1p, | |
436 | + (double)x2p, (double)y2p, | |
437 | + (double)x3p, (double)y3p); | |
438 | + } | |
439 | + | |
440 | + /** | |
441 | + * Returns the determinant of the matrix representation of the | |
442 | + * transform. | |
443 | + */ | |
444 | + public double getDeterminant() { | |
445 | + return ( (m00 * ((m11 * m22) - (m12 * m21))) - | |
446 | + (m01 * ((m10 * m22) - (m12 * m20))) + | |
447 | + (m02 * ((m10 * m21) - (m11 * m20))) ); | |
448 | + | |
449 | + } | |
450 | + | |
451 | + /** | |
452 | + * Retrieves the 9 specifiable values in the 3x3 affine | |
453 | + * transformation matrix into an array of double precision values. | |
454 | + * The values are stored into the array as | |
455 | + * { m00 m01 m02 m10 m11 m12 m20 m21 m22 }. | |
456 | + * | |
457 | + * @param flatmatrix The double array used to store the returned | |
458 | + * values. The length of the array is assumed to be at | |
459 | + * least 9. | |
460 | + * @throws ArrayIndexOutOfBoundsException if flatmatrix is too small | |
461 | + * @deprecated as of JAI 1.1 Use double[][] getMatrix(double[][] matrix) instead. | |
462 | + */ | |
463 | + public double[] getMatrix(double[] flatmatrix) { | |
464 | + if (flatmatrix == null) { | |
465 | + flatmatrix = new double[9]; | |
466 | + } | |
467 | + | |
468 | + flatmatrix[0] = m00; | |
469 | + flatmatrix[1] = m01; | |
470 | + flatmatrix[2] = m02; | |
471 | + flatmatrix[3] = m10; | |
472 | + flatmatrix[4] = m11; | |
473 | + flatmatrix[5] = m12; | |
474 | + flatmatrix[6] = m20; | |
475 | + flatmatrix[7] = m21; | |
476 | + flatmatrix[8] = m22; | |
477 | + | |
478 | + return flatmatrix; | |
479 | + } | |
480 | + | |
481 | + /** | |
482 | + * Retrieves the 9 specifiable values in the 3x3 affine | |
483 | + * transformation matrix into a 2-dimensional array of double | |
484 | + * precision values. The values are stored into the 2-dimensional | |
485 | + * array using the row index as the first subscript and the column | |
486 | + * index as the second. | |
487 | + * | |
488 | + * @param matrix The 2-dimensional double array to store the | |
489 | + * returned values. The array is assumed to be at least 3x3. | |
490 | + * @throws ArrayIndexOutOfBoundsException if matrix is too small | |
491 | + */ | |
492 | + public double[][] getMatrix(double[][] matrix) { | |
493 | + if (matrix == null) { | |
494 | + matrix = new double[3][3]; | |
495 | + } | |
496 | + | |
497 | + matrix[0][0] = m00; | |
498 | + matrix[0][1] = m01; | |
499 | + matrix[0][2] = m02; | |
500 | + matrix[1][0] = m10; | |
501 | + matrix[1][1] = m11; | |
502 | + matrix[1][2] = m12; | |
503 | + matrix[2][0] = m20; | |
504 | + matrix[2][1] = m21; | |
505 | + matrix[2][2] = m22; | |
506 | + | |
507 | + return matrix; | |
508 | + } | |
509 | + | |
510 | + /** | |
511 | + * Concatenates this transform with a translation transformation. | |
512 | + * This is equivalent to calling concatenate(T), where T is an | |
513 | + * PerspectiveTransform represented by the following matrix: | |
514 | + * <pre> | |
515 | + * [ 1 0 tx ] | |
516 | + * [ 0 1 ty ] | |
517 | + * [ 0 0 1 ] | |
518 | + * </pre> | |
519 | + */ | |
520 | + public void translate(double tx, double ty) { | |
521 | + PerspectiveTransform Tx = new PerspectiveTransform(); | |
522 | + Tx.setToTranslation(tx, ty); | |
523 | + concatenate(Tx); | |
524 | + } | |
525 | + | |
526 | + /** | |
527 | + * Concatenates this transform with a rotation transformation. | |
528 | + * This is equivalent to calling concatenate(R), where R is an | |
529 | + * PerspectiveTransform represented by the following matrix: | |
530 | + * <pre> | |
531 | + * [ cos(theta) -sin(theta) 0 ] | |
532 | + * [ sin(theta) cos(theta) 0 ] | |
533 | + * [ 0 0 1 ] | |
534 | + * </pre> | |
535 | + * Rotating with a positive angle theta rotates points on the positive | |
536 | + * X axis toward the positive Y axis. | |
537 | + * | |
538 | + * @param theta The angle of rotation in radians. | |
539 | + */ | |
540 | + public void rotate(double theta) { | |
541 | + PerspectiveTransform Tx = new PerspectiveTransform(); | |
542 | + Tx.setToRotation(theta); | |
543 | + concatenate(Tx); | |
544 | + } | |
545 | + | |
546 | + /** | |
547 | + * Concatenates this transform with a translated rotation transformation. | |
548 | + * This is equivalent to the following sequence of calls: | |
549 | + * <pre> | |
550 | + * translate(x, y); | |
551 | + * rotate(theta); | |
552 | + * translate(-x, -y); | |
553 | + * </pre> | |
554 | + * Rotating with a positive angle theta rotates points on the positive | |
555 | + * X axis toward the positive Y axis. | |
556 | + * | |
557 | + * @param theta The angle of rotation in radians. | |
558 | + * @param x The X coordinate of the origin of the rotation | |
559 | + * @param y The Y coordinate of the origin of the rotation | |
560 | + */ | |
561 | + public void rotate(double theta, double x, double y) { | |
562 | + PerspectiveTransform Tx = new PerspectiveTransform(); | |
563 | + Tx.setToRotation(theta, x, y); | |
564 | + concatenate(Tx); | |
565 | + } | |
566 | + | |
567 | + /** | |
568 | + * Concatenates this transform with a scaling transformation. | |
569 | + * This is equivalent to calling concatenate(S), where S is an | |
570 | + * PerspectiveTransform represented by the following matrix: | |
571 | + * <pre> | |
572 | + * [ sx 0 0 ] | |
573 | + * [ 0 sy 0 ] | |
574 | + * [ 0 0 1 ] | |
575 | + * </pre> | |
576 | + * | |
577 | + * @param sx The X axis scale factor. | |
578 | + * @param sy The Y axis scale factor. | |
579 | + */ | |
580 | + public void scale(double sx, double sy) { | |
581 | + PerspectiveTransform Tx = new PerspectiveTransform(); | |
582 | + Tx.setToScale(sx, sy); | |
583 | + concatenate(Tx); | |
584 | + } | |
585 | + | |
586 | + /** | |
587 | + * Concatenates this transform with a shearing transformation. | |
588 | + * This is equivalent to calling concatenate(SH), where SH is an | |
589 | + * PerspectiveTransform represented by the following matrix: | |
590 | + * <pre> | |
591 | + * [ 1 shx 0 ] | |
592 | + * [ shy 1 0 ] | |
593 | + * [ 0 0 1 ] | |
594 | + * </pre> | |
595 | + * | |
596 | + * @param shx The factor by which coordinates are shifted towards | |
597 | + * the positive X axis direction according to their Y | |
598 | + * coordinate. | |
599 | + * @param shy The factor by which coordinates are shifted towards | |
600 | + * the positive Y axis direction according to their X | |
601 | + * coordinate. | |
602 | + */ | |
603 | + public void shear(double shx, double shy) { | |
604 | + PerspectiveTransform Tx = new PerspectiveTransform(); | |
605 | + Tx.setToShear(shx, shy); | |
606 | + concatenate(Tx); | |
607 | + } | |
608 | + | |
609 | + /** | |
610 | + * Resets this transform to the Identity transform. | |
611 | + */ | |
612 | + public void setToIdentity() { | |
613 | + m00 = m11 = m22 = 1.0; | |
614 | + m01 = m10 = m02 = m20 = m12 = m21 = 0.0; | |
615 | + } | |
616 | + | |
617 | + /** | |
618 | + * Sets this transform to a translation transformation. | |
619 | + * The matrix representing this transform becomes: | |
620 | + * <pre> | |
621 | + * [ 1 0 tx ] | |
622 | + * [ 0 1 ty ] | |
623 | + * [ 0 0 1 ] | |
624 | + * </pre> | |
625 | + * @param tx The distance by which coordinates are translated in the | |
626 | + * X axis direction | |
627 | + * @param ty The distance by which coordinates are translated in the | |
628 | + * Y axis direction | |
629 | + */ | |
630 | + public void setToTranslation(double tx, double ty) { | |
631 | + m00 = 1.0; | |
632 | + m01 = 0.0; | |
633 | + m02 = tx; | |
634 | + m10 = 0.0; | |
635 | + m11 = 1.0; | |
636 | + m12 = ty; | |
637 | + m20 = 0.0; | |
638 | + m21 = 0.0; | |
639 | + m22 = 1.0; | |
640 | + } | |
641 | + | |
642 | + /** | |
643 | + * Sets this transform to a rotation transformation. | |
644 | + * The matrix representing this transform becomes: | |
645 | + * <pre> | |
646 | + * [ cos(theta) -sin(theta) 0 ] | |
647 | + * [ sin(theta) cos(theta) 0 ] | |
648 | + * [ 0 0 1 ] | |
649 | + * </pre> | |
650 | + * Rotating with a positive angle theta rotates points on the positive | |
651 | + * X axis toward the positive Y axis. | |
652 | + * @param theta The angle of rotation in radians. | |
653 | + */ | |
654 | + public void setToRotation(double theta) { | |
655 | + m00 = Math.cos(theta); | |
656 | + m01 = -Math.sin(theta); | |
657 | + m02 = 0.0; | |
658 | + m10 = - m01; // Math.sin(theta); | |
659 | + m11 = m00; // Math.cos(theta); | |
660 | + m12 = 0.0; | |
661 | + m20 = 0.0; | |
662 | + m21 = 0.0; | |
663 | + m22 = 1.0; | |
664 | + } | |
665 | + | |
666 | + /** | |
667 | + * Sets this transform to a rotation transformation | |
668 | + * about a specified point (x, y). This is equivalent | |
669 | + * to the following sequence of calls: | |
670 | + * | |
671 | + * <pre> | |
672 | + * setToTranslate(x, y); | |
673 | + * rotate(theta); | |
674 | + * translate(-x, -y); | |
675 | + * </pre> | |
676 | + * | |
677 | + * Rotating with a positive angle theta rotates points on the positive | |
678 | + * X axis toward the positive Y axis. | |
679 | + * | |
680 | + * @param theta The angle of rotation in radians. | |
681 | + * @param x The X coordinate of the origin of the rotation | |
682 | + * @param y The Y coordinate of the origin of the rotation | |
683 | + */ | |
684 | + public void setToRotation(double theta, double x, double y) { | |
685 | + setToRotation(theta); | |
686 | + double sin = m10; | |
687 | + double oneMinusCos = 1.0 - m00; | |
688 | + m02 = x * oneMinusCos + y * sin; | |
689 | + m12 = y * oneMinusCos - x * sin; | |
690 | + } | |
691 | + | |
692 | + /** | |
693 | + * Sets this transform to a scale transformation | |
694 | + * with scale factors sx and sy. | |
695 | + * The matrix representing this transform becomes: | |
696 | + * <pre> | |
697 | + * [ sx 0 0 ] | |
698 | + * [ 0 sy 0 ] | |
699 | + * [ 0 0 1 ] | |
700 | + * </pre> | |
701 | + * | |
702 | + * @param sx The X axis scale factor. | |
703 | + * @param sy The Y axis scale factor. | |
704 | + */ | |
705 | + public void setToScale(double sx, double sy) { | |
706 | + m00 = sx; | |
707 | + m01 = 0.0; | |
708 | + m02 = 0.0; | |
709 | + m10 = 0.0; | |
710 | + m11 = sy; | |
711 | + m12 = 0.0; | |
712 | + m20 = 0.0; | |
713 | + m21 = 0.0; | |
714 | + m22 = 1.0; | |
715 | + } | |
716 | + | |
717 | + /** | |
718 | + * Sets this transform to a shearing transformation | |
719 | + * with shear factors sx and sy. | |
720 | + * The matrix representing this transform becomes: | |
721 | + * <pre> | |
722 | + * [ 1 shx 0 ] | |
723 | + * [ shy 1 0 ] | |
724 | + * [ 0 0 1 ] | |
725 | + * </pre> | |
726 | + * | |
727 | + * @param shx The factor by which coordinates are shifted towards | |
728 | + * the positive X axis direction according to their Y | |
729 | + * coordinate. | |
730 | + * @param shy The factor by which coordinates are shifted towards | |
731 | + * the positive Y axis direction according to their X | |
732 | + * coordinate. | |
733 | + */ | |
734 | + public void setToShear(double shx, double shy) { | |
735 | + m00 = 1.0; | |
736 | + m01 = shx; | |
737 | + m02 = 0.0; | |
738 | + m10 = shy; | |
739 | + m11 = 1.0; | |
740 | + m12 = 0.0; | |
741 | + m20 = 0.0; | |
742 | + m21 = 0.0; | |
743 | + m22 = 1.0; | |
744 | + } | |
745 | + | |
746 | + /** | |
747 | + * Sets this transform to a given AffineTransform. | |
748 | + * @throws IllegalArgumentException if Tx is null | |
749 | + */ | |
750 | + public void setTransform(AffineTransform Tx) { | |
751 | + if ( Tx == null ) { | |
752 | + throw new IllegalArgumentException(JaiI18N.getString("Generic0")); | |
753 | + } | |
754 | + | |
755 | + m00 = Tx.getScaleX(); | |
756 | + m01 = Tx.getShearX(); | |
757 | + m02 = Tx.getTranslateX(); | |
758 | + m10 = Tx.getShearY(); | |
759 | + m11 = Tx.getScaleY(); | |
760 | + m12 = Tx.getTranslateY(); | |
761 | + m20 = 0.0; | |
762 | + m21 = 0.0; | |
763 | + m22 = 1.0; | |
764 | + } | |
765 | + | |
766 | + /** | |
767 | + * Sets this transform to a given PerspectiveTransform. | |
768 | + * @throws IllegalArgumentException if Tx is null | |
769 | + */ | |
770 | + public void setTransform(PerspectiveTransform Tx) { | |
771 | + if ( Tx == null ) { | |
772 | + throw new IllegalArgumentException(JaiI18N.getString("Generic0")); | |
773 | + } | |
774 | + | |
775 | + m00 = Tx.m00; | |
776 | + m01 = Tx.m01; | |
777 | + m02 = Tx.m02; | |
778 | + m10 = Tx.m10; | |
779 | + m11 = Tx.m11; | |
780 | + m12 = Tx.m12; | |
781 | + m20 = Tx.m20; | |
782 | + m21 = Tx.m21; | |
783 | + m22 = Tx.m22; | |
784 | + } | |
785 | + | |
786 | + /** | |
787 | + * Sets this transform to a given PerspectiveTransform, | |
788 | + * expressed by the elements of its matrix. <i>Important Note: The | |
789 | + * matrix elements in the argument list are in column-major order | |
790 | + * unlike those of the constructor, which are in row-major order.</i> | |
791 | + * @deprecated as of JAI 1.1 Use double[][] getMatrix(double[][] matrix) instead. | |
792 | + */ | |
793 | + public void setTransform(float m00, float m10, float m20, | |
794 | + float m01, float m11, float m21, | |
795 | + float m02, float m12, float m22) { | |
796 | + this.m00 = (double)m00; | |
797 | + this.m01 = (double)m01; | |
798 | + this.m02 = (double)m02; | |
799 | + this.m10 = (double)m10; | |
800 | + this.m11 = (double)m11; | |
801 | + this.m12 = (double)m12; | |
802 | + this.m20 = (double)m20; | |
803 | + this.m21 = (double)m21; | |
804 | + this.m22 = (double)m22; | |
805 | + } | |
806 | + | |
807 | + /** | |
808 | + * Sets this transform using a two-dimensional array of double precision | |
809 | + * values. The row index is first, and the column index is second. | |
810 | + * | |
811 | + * @param matrix The 2D double array to be used for setting this transform. | |
812 | + * The array is assumed to be at least 3x3. | |
813 | + * @throws IllegalArgumentException if matrix is null | |
814 | + * @throws ArrayIndexOutOfBoundsException if matrix is too small | |
815 | + * @since JAI 1.1 | |
816 | + */ | |
817 | + public void setTransform(double[][] matrix) { | |
818 | + if ( matrix == null ) { | |
819 | + throw new IllegalArgumentException(JaiI18N.getString("Generic0")); | |
820 | + } | |
821 | + | |
822 | + m00 = matrix[0][0]; | |
823 | + m01 = matrix[0][1]; | |
824 | + m02 = matrix[0][2]; | |
825 | + m10 = matrix[1][0]; | |
826 | + m11 = matrix[1][1]; | |
827 | + m12 = matrix[1][2]; | |
828 | + m20 = matrix[2][0]; | |
829 | + m21 = matrix[2][1]; | |
830 | + m22 = matrix[2][2]; | |
831 | + } | |
832 | + | |
833 | + /** | |
834 | + * Post-concatenates a given AffineTransform to this transform. | |
835 | + * @throws IllegalArgumentException if Tx is null | |
836 | + */ | |
837 | + public void concatenate(AffineTransform Tx) { | |
838 | + if ( Tx == null ) { | |
839 | + throw new IllegalArgumentException(JaiI18N.getString("Generic0")); | |
840 | + } | |
841 | + | |
842 | + // Extend Tx: Tx.m20 = 0, Tx.m21 = 0, Tx.m22 = 1 | |
843 | + | |
844 | + double tx_m00 = Tx.getScaleX(); | |
845 | + double tx_m01 = Tx.getShearX(); | |
846 | + double tx_m02 = Tx.getTranslateX(); | |
847 | + double tx_m10 = Tx.getShearY(); | |
848 | + double tx_m11 = Tx.getScaleY(); | |
849 | + double tx_m12 = Tx.getTranslateY(); | |
850 | + | |
851 | + double m00p = m00*tx_m00 + m10*tx_m01 + m20*tx_m02; | |
852 | + double m01p = m01*tx_m00 + m11*tx_m01 + m21*tx_m02; | |
853 | + double m02p = m02*tx_m00 + m12*tx_m01 + m22*tx_m02; | |
854 | + double m10p = m00*tx_m10 + m10*tx_m11 + m20*tx_m12; | |
855 | + double m11p = m01*tx_m10 + m11*tx_m11 + m21*tx_m12; | |
856 | + double m12p = m02*tx_m10 + m12*tx_m11 + m22*tx_m12; | |
857 | + double m20p = m20; | |
858 | + double m21p = m21; | |
859 | + double m22p = m22; | |
860 | + | |
861 | + m00 = m00p; | |
862 | + m10 = m10p; | |
863 | + m20 = m20p; | |
864 | + m01 = m01p; | |
865 | + m11 = m11p; | |
866 | + m21 = m21p; | |
867 | + m02 = m02p; | |
868 | + m12 = m12p; | |
869 | + m22 = m22p; | |
870 | + } | |
871 | + | |
872 | + /** | |
873 | + * Post-concatenates a given PerspectiveTransform to this transform. | |
874 | + * @throws IllegalArgumentException if Tx is null | |
875 | + */ | |
876 | + public void concatenate(PerspectiveTransform Tx) { | |
877 | + if ( Tx == null ) { | |
878 | + throw new IllegalArgumentException(JaiI18N.getString("Generic0")); | |
879 | + } | |
880 | + | |
881 | + double m00p = m00*Tx.m00 + m10*Tx.m01 + m20*Tx.m02; | |
882 | + double m10p = m00*Tx.m10 + m10*Tx.m11 + m20*Tx.m12; | |
883 | + double m20p = m00*Tx.m20 + m10*Tx.m21 + m20*Tx.m22; | |
884 | + double m01p = m01*Tx.m00 + m11*Tx.m01 + m21*Tx.m02; | |
885 | + double m11p = m01*Tx.m10 + m11*Tx.m11 + m21*Tx.m12; | |
886 | + double m21p = m01*Tx.m20 + m11*Tx.m21 + m21*Tx.m22; | |
887 | + double m02p = m02*Tx.m00 + m12*Tx.m01 + m22*Tx.m02; | |
888 | + double m12p = m02*Tx.m10 + m12*Tx.m11 + m22*Tx.m12; | |
889 | + double m22p = m02*Tx.m20 + m12*Tx.m21 + m22*Tx.m22; | |
890 | + | |
891 | + m00 = m00p; | |
892 | + m10 = m10p; | |
893 | + m20 = m20p; | |
894 | + m01 = m01p; | |
895 | + m11 = m11p; | |
896 | + m21 = m21p; | |
897 | + m02 = m02p; | |
898 | + m12 = m12p; | |
899 | + m22 = m22p; | |
900 | + } | |
901 | + | |
902 | + /** | |
903 | + * Pre-concatenates a given AffineTransform to this transform. | |
904 | + * @throws IllegalArgumentException if Tx is null | |
905 | + */ | |
906 | + public void preConcatenate(AffineTransform Tx) { | |
907 | + if ( Tx == null ) { | |
908 | + throw new IllegalArgumentException(JaiI18N.getString("Generic0")); | |
909 | + } | |
910 | + | |
911 | + // Extend Tx: Tx.m20 = 0, Tx.m21 = 0, Tx.m22 = 1 | |
912 | + | |
913 | + double tx_m00 = Tx.getScaleX(); | |
914 | + double tx_m01 = Tx.getShearX(); | |
915 | + double tx_m02 = Tx.getTranslateX(); | |
916 | + double tx_m10 = Tx.getShearY(); | |
917 | + double tx_m11 = Tx.getScaleY(); | |
918 | + double tx_m12 = Tx.getTranslateY(); | |
919 | + | |
920 | + double m00p = tx_m00*m00 + tx_m10*m01; | |
921 | + double m01p = tx_m01*m00 + tx_m11*m01; | |
922 | + double m02p = tx_m02*m00 + tx_m12*m01 + m02; | |
923 | + double m10p = tx_m00*m10 + tx_m10*m11; | |
924 | + double m11p = tx_m01*m10 + tx_m11*m11; | |
925 | + double m12p = tx_m02*m10 + tx_m12*m11 + m12; | |
926 | + double m20p = tx_m00*m20 + tx_m10*m21; | |
927 | + double m21p = tx_m01*m20 + tx_m11*m21; | |
928 | + double m22p = tx_m02*m20 + tx_m12*m21 + m22; | |
929 | + | |
930 | + m00 = m00p; | |
931 | + m10 = m10p; | |
932 | + m20 = m20p; | |
933 | + m01 = m01p; | |
934 | + m11 = m11p; | |
935 | + m21 = m21p; | |
936 | + m02 = m02p; | |
937 | + m12 = m12p; | |
938 | + m22 = m22p; | |
939 | + } | |
940 | + | |
941 | + /** | |
942 | + * Pre-concatenates a given PerspectiveTransform to this transform. | |
943 | + * @throws IllegalArgumentException if Tx is null | |
944 | + */ | |
945 | + public void preConcatenate(PerspectiveTransform Tx) { | |
946 | + if ( Tx == null ) { | |
947 | + throw new IllegalArgumentException(JaiI18N.getString("Generic0")); | |
948 | + } | |
949 | + | |
950 | + double m00p = Tx.m00*m00 + Tx.m10*m01 + Tx.m20*m02; | |
951 | + double m10p = Tx.m00*m10 + Tx.m10*m11 + Tx.m20*m12; | |
952 | + double m20p = Tx.m00*m20 + Tx.m10*m21 + Tx.m20*m22; | |
953 | + double m01p = Tx.m01*m00 + Tx.m11*m01 + Tx.m21*m02; | |
954 | + double m11p = Tx.m01*m10 + Tx.m11*m11 + Tx.m21*m12; | |
955 | + double m21p = Tx.m01*m20 + Tx.m11*m21 + Tx.m21*m22; | |
956 | + double m02p = Tx.m02*m00 + Tx.m12*m01 + Tx.m22*m02; | |
957 | + double m12p = Tx.m02*m10 + Tx.m12*m11 + Tx.m22*m12; | |
958 | + double m22p = Tx.m02*m20 + Tx.m12*m21 + Tx.m22*m22; | |
959 | + | |
960 | + m00 = m00p; | |
961 | + m10 = m10p; | |
962 | + m20 = m20p; | |
963 | + m01 = m01p; | |
964 | + m11 = m11p; | |
965 | + m21 = m21p; | |
966 | + m02 = m02p; | |
967 | + m12 = m12p; | |
968 | + m22 = m22p; | |
969 | + } | |
970 | + | |
971 | + /** | |
972 | + * Returns a new PerpectiveTransform that is the inverse | |
973 | + * of the current transform. | |
974 | + * @throws NoninvertibleTransformException if transform cannot be inverted | |
975 | + */ | |
976 | + public PerspectiveTransform createInverse() | |
977 | + throws NoninvertibleTransformException, CloneNotSupportedException { | |
978 | + | |
979 | + PerspectiveTransform tx = (PerspectiveTransform)clone(); | |
980 | + tx.makeAdjoint(); | |
981 | + if (Math.abs(tx.m22) < PERSPECTIVE_DIVIDE_EPSILON) { | |
982 | + throw new NoninvertibleTransformException(JaiI18N.getString("PerspectiveTransform0")); | |
983 | + } | |
984 | + tx.normalize(); | |
985 | + return tx; | |
986 | + } | |
987 | + | |
988 | + /** | |
989 | + * Returns a new PerpectiveTransform that is the adjoint, | |
990 | + * of the current transform. The adjoint is defined as | |
991 | + * the matrix of cofactors, which in turn are the determinants | |
992 | + * of the submatrices defined by removing the row and column | |
993 | + * of each element from the original matrix in turn. | |
994 | + * | |
995 | + * <p> The adjoint is a scalar multiple of the inverse matrix. | |
996 | + * Because points to be transformed are converted into homogeneous | |
997 | + * coordinates, where scalar factors are irrelevant, the adjoint | |
998 | + * may be used in place of the true inverse. Since it is unnecessary | |
999 | + * to normalize the adjoint, it is both faster to compute and more | |
1000 | + * numerically stable than the true inverse. | |
1001 | + */ | |
1002 | + public PerspectiveTransform createAdjoint() | |
1003 | + throws CloneNotSupportedException{ | |
1004 | + | |
1005 | + PerspectiveTransform tx = (PerspectiveTransform)clone(); | |
1006 | + tx.makeAdjoint(); | |
1007 | + return tx; | |
1008 | + } | |
1009 | + | |
1010 | + /** | |
1011 | + * Transforms the specified ptSrc and stores the result in ptDst. | |
1012 | + * If ptDst is null, a new Point2D object will be allocated before | |
1013 | + * storing. In either case, ptDst containing the transformed point | |
1014 | + * is returned for convenience. | |
1015 | + * Note that ptSrc and ptDst can the same. In this case, the input | |
1016 | + * point will be overwritten with the transformed point. | |
1017 | + * | |
1018 | + * @param ptSrc The array containing the source point objects. | |
1019 | + * @param ptDst The array where the transform point objects are returned. | |
1020 | + * @throws IllegalArgumentException if ptSrc is null | |
1021 | + */ | |
1022 | + public Point2D transform(Point2D ptSrc, Point2D ptDst) { | |
1023 | + if ( ptSrc == null ) { | |
1024 | + throw new IllegalArgumentException(JaiI18N.getString("Generic0")); | |
1025 | + } | |
1026 | + | |
1027 | + if (ptDst == null) { | |
1028 | + if (ptSrc instanceof Point2D.Double) { | |
1029 | + ptDst = new Point2D.Double(); | |
1030 | + } else { | |
1031 | + ptDst = new Point2D.Float(); | |
1032 | + } | |
1033 | + } | |
1034 | + | |
1035 | + double x = ptSrc.getX(); | |
1036 | + double y = ptSrc.getY(); | |
1037 | + double w = m20 * x + m21 * y + m22; | |
1038 | + ptDst.setLocation((m00 * x + m01 * y + m02) / w, | |
1039 | + (m10 * x + m11 * y + m12) / w); | |
1040 | + | |
1041 | + return ptDst; | |
1042 | + } | |
1043 | + | |
1044 | + /** | |
1045 | + * Transforms an array of point objects by this transform. | |
1046 | + * @param ptSrc The array containing the source point objects. | |
1047 | + * @param ptDst The array where the transform point objects are returned. | |
1048 | + * @param srcOff The offset to the first point object to be transformed | |
1049 | + * in the source array. | |
1050 | + * @param dstOff The offset to the location where the first transformed | |
1051 | + * point object is stored in the destination array. | |
1052 | + * @param numPts The number of point objects to be transformed. | |
1053 | + * @throws IllegalArgumentException if ptSrc is null | |
1054 | + * @throws IllegalArgumentException if ptDst is null | |
1055 | + * @throws ArrayIndexOutOfBoundsException if ptSrc is too small | |
1056 | + */ | |
1057 | + public void transform(Point2D[] ptSrc, int srcOff, | |
1058 | + Point2D[] ptDst, int dstOff, | |
1059 | + int numPts) { | |
1060 | + | |
1061 | + if ( ptSrc == null || ptDst == null ) { | |
1062 | + throw new IllegalArgumentException(JaiI18N.getString("Generic0")); | |
1063 | + } | |
1064 | + | |
1065 | + while (numPts-- > 0) { | |
1066 | + /* Copy source coords into local variables in case src == dst. */ | |
1067 | + Point2D src = ptSrc[srcOff++]; | |
1068 | + Point2D dst = ptDst[dstOff++]; | |
1069 | + if (dst == null) { | |
1070 | + if (src instanceof Point2D.Double) { | |
1071 | + dst = new Point2D.Double(); | |
1072 | + } else { | |
1073 | + dst = new Point2D.Float(); | |
1074 | + } | |
1075 | + ptDst[dstOff - 1] = dst; | |
1076 | + } | |
1077 | + | |
1078 | + double x = src.getX(); | |
1079 | + double y = src.getY(); | |
1080 | + double w = m20 * x + m21 * y + m22; | |
1081 | + | |
1082 | + if (w == 0) { | |
1083 | + dst.setLocation(x, y); | |
1084 | + } else { | |
1085 | + dst.setLocation((m00 * x + m01 * y + m02) / w, | |
1086 | + (m10 * x + m11 * y + m12) / w); | |
1087 | + } | |
1088 | + } | |
1089 | + } | |
1090 | + | |
1091 | + /** | |
1092 | + * Transforms an array of floating point coordinates by this transform. | |
1093 | + * @param srcPts The array containing the source point coordinates. | |
1094 | + * Each point is stored as a pair of x,y coordinates. | |
1095 | + * @param srcOff The offset to the first point to be transformed | |
1096 | + * in the source array. | |
1097 | + * @param dstPts The array where the transformed point coordinates are | |
1098 | + * returned. Each point is stored as a pair of x,y coordinates. | |
1099 | + * @param dstOff The offset to the location where the first transformed | |
1100 | + * point is stored in the destination array. | |
1101 | + * @param numPts The number of points to be transformed. | |
1102 | + * @throws IllegalArgumentException if srcPts is null | |
1103 | + * @throws ArrayIndexOutOfBoundsException if srcPts is too small | |
1104 | + */ | |
1105 | + public void transform(float[] srcPts, int srcOff, | |
1106 | + float[] dstPts, int dstOff, | |
1107 | + int numPts) { | |
1108 | + | |
1109 | + if ( srcPts == null ) { | |
1110 | + throw new IllegalArgumentException(JaiI18N.getString("Generic0")); | |
1111 | + } | |
1112 | + | |
1113 | + if (dstPts == null) { | |
1114 | + dstPts = new float[numPts * 2 + dstOff]; | |
1115 | + } | |
1116 | + | |
1117 | + while (numPts-- > 0) { | |
1118 | + float x = srcPts[srcOff++]; | |
1119 | + float y = srcPts[srcOff++]; | |
1120 | + double w = m20 * x + m21 * y + m22; | |
1121 | + | |
1122 | + if (w == 0) { | |
1123 | + dstPts[dstOff++] = x; | |
1124 | + dstPts[dstOff++] = y; | |
1125 | + } else { | |
1126 | + dstPts[dstOff++] = (float)((m00 * x + m01 * y + m02) / w); | |
1127 | + dstPts[dstOff++] = (float)((m10 * x + m11 * y + m12) / w); | |
1128 | + } | |
1129 | + } | |
1130 | + } | |
1131 | + | |
1132 | + /** | |
1133 | + * Transforms an array of double precision coordinates by this transform. | |
1134 | + * @param srcPts The array containing the source point coordinates. | |
1135 | + * Each point is stored as a pair of x,y coordinates. | |
1136 | + * @param dstPts The array where the transformed point coordinates are | |
1137 | + * returned. Each point is stored as a pair of x,y coordinates. | |
1138 | + * @param srcOff The offset to the first point to be transformed | |
1139 | + * in the source array. | |
1140 | + * @param dstOff The offset to the location where the first transformed | |
1141 | + * point is stored in the destination array. | |
1142 | + * @param numPts The number of point objects to be transformed. | |
1143 | + * @throws IllegalArgumentException if srcPts is null | |
1144 | + * @throws ArrayIndexOutOfBoundsException if srcPts is too small | |
1145 | + */ | |
1146 | + public void transform(double[] srcPts, int srcOff, | |
1147 | + double[] dstPts, int dstOff, | |
1148 | + int numPts) { | |
1149 | + | |
1150 | + if ( srcPts == null ) { | |
1151 | + throw new IllegalArgumentException(JaiI18N.getString("Generic0")); | |
1152 | + } | |
1153 | + | |
1154 | + if (dstPts == null) { | |
1155 | + dstPts = new double[numPts * 2 + dstOff]; | |
1156 | + } | |
1157 | + | |
1158 | + while (numPts-- > 0) { | |
1159 | + double x = srcPts[srcOff++]; | |
1160 | + double y = srcPts[srcOff++]; | |
1161 | + double w = m20 * x + m21 * y + m22; | |
1162 | + | |
1163 | + if (w == 0) { | |
1164 | + dstPts[dstOff++] = x; | |
1165 | + dstPts[dstOff++] = y; | |
1166 | + } else { | |
1167 | + dstPts[dstOff++] = (m00 * x + m01 * y + m02) / w; | |
1168 | + dstPts[dstOff++] = (m10 * x + m11 * y + m12) / w; | |
1169 | + } | |
1170 | + } | |
1171 | + } | |
1172 | + | |
1173 | + /** | |
1174 | + * Transforms an array of floating point coordinates by this transform, | |
1175 | + * storing the results into an array of doubles. | |
1176 | + * @param srcPts The array containing the source point coordinates. | |
1177 | + * Each point is stored as a pair of x,y coordinates. | |
1178 | + * @param srcOff The offset to the first point to be transformed | |
1179 | + * in the source array. | |
1180 | + * @param dstPts The array where the transformed point coordinates are | |
1181 | + * returned. Each point is stored as a pair of x,y coordinates. | |
1182 | + * @param dstOff The offset to the location where the first transformed | |
1183 | + * point is stored in the destination array. | |
1184 | + * @param numPts The number of points to be transformed. | |
1185 | + * @throws IllegalArgumentException if srcPts is null | |
1186 | + * @throws ArrayIndexOutOfBoundsException if srcPts is too small | |
1187 | + */ | |
1188 | + public void transform(float[] srcPts, int srcOff, | |
1189 | + double[] dstPts, int dstOff, | |
1190 | + int numPts) { | |
1191 | + | |
1192 | + if ( srcPts == null ) { | |
1193 | + throw new IllegalArgumentException(JaiI18N.getString("Generic0")); | |
1194 | + } | |
1195 | + | |
1196 | + if (dstPts == null) { | |
1197 | + dstPts = new double[numPts * 2 + dstOff]; | |
1198 | + } | |
1199 | + | |
1200 | + while (numPts-- > 0) { | |
1201 | + float x = srcPts[srcOff++]; | |
1202 | + float y = srcPts[srcOff++]; | |
1203 | + double w = m20 * x + m21 * y + m22; | |
1204 | + | |
1205 | + if (w == 0) { | |
1206 | + dstPts[dstOff++] = x; | |
1207 | + dstPts[dstOff++] = y; | |
1208 | + } else { | |
1209 | + dstPts[dstOff++] = (m00 * x + m01 * y + m02) / w; | |
1210 | + dstPts[dstOff++] = (m10 * x + m11 * y + m12) / w; | |
1211 | + } | |
1212 | + } | |
1213 | + } | |
1214 | + | |
1215 | + /** | |
1216 | + * Transforms an array of double precision coordinates by this transform, | |
1217 | + * storing the results into an array of floats. | |
1218 | + * @param srcPts The array containing the source point coordinates. | |
1219 | + * Each point is stored as a pair of x,y coordinates. | |
1220 | + * @param dstPts The array where the transformed point coordinates are | |
1221 | + * returned. Each point is stored as a pair of x,y coordinates. | |
1222 | + * @param srcOff The offset to the first point to be transformed | |
1223 | + * in the source array. | |
1224 | + * @param dstOff The offset to the location where the first transformed | |
1225 | + * point is stored in the destination array. | |
1226 | + * @param numPts The number of point objects to be transformed. | |
1227 | + * @throws IllegalArgumentException if srcPts is null | |
1228 | + * @throws ArrayIndexOutOfBoundsException if srcPts is too small | |
1229 | + */ | |
1230 | + public void transform(double[] srcPts, int srcOff, | |
1231 | + float[] dstPts, int dstOff, | |
1232 | + int numPts) { | |
1233 | + | |
1234 | + if ( srcPts == null ) { | |
1235 | + throw new IllegalArgumentException(JaiI18N.getString("Generic0")); | |
1236 | + } | |
1237 | + | |
1238 | + if (dstPts == null) { | |
1239 | + dstPts = new float[numPts * 2 + dstOff]; | |
1240 | + } | |
1241 | + | |
1242 | + while (numPts-- > 0) { | |
1243 | + double x = srcPts[srcOff++]; | |
1244 | + double y = srcPts[srcOff++]; | |
1245 | + double w = m20 * x + m21 * y + m22; | |
1246 | + | |
1247 | + if (w == 0) { | |
1248 | + dstPts[dstOff++] = (float)x; | |
1249 | + dstPts[dstOff++] = (float)y; | |
1250 | + } else { | |
1251 | + dstPts[dstOff++] = (float)((m00 * x + m01 * y + m02) / w); | |
1252 | + dstPts[dstOff++] = (float)((m10 * x + m11 * y + m12) / w); | |
1253 | + } | |
1254 | + } | |
1255 | + } | |
1256 | + | |
1257 | + /** | |
1258 | + * Inverse transforms the specified ptSrc and stores the result in ptDst. | |
1259 | + * If ptDst is null, a new Point2D object will be allocated before | |
1260 | + * storing. In either case, ptDst containing the transformed point | |
1261 | + * is returned for convenience. | |
1262 | + * Note that ptSrc and ptDst can the same. In this case, the input | |
1263 | + * point will be overwritten with the transformed point. | |
1264 | + * @param ptSrc The point to be inverse transformed. | |
1265 | + * @param ptDst The resulting transformed point. | |
1266 | + * @throws NoninvertibleTransformException if the matrix cannot be | |
1267 | + * inverted. | |
1268 | + * @throws IllegalArgumentException if ptSrc is null | |
1269 | + */ | |
1270 | + public Point2D inverseTransform(Point2D ptSrc, Point2D ptDst) | |
1271 | + throws NoninvertibleTransformException | |
1272 | + { | |
1273 | + if ( ptSrc == null ) { | |
1274 | + throw new IllegalArgumentException(JaiI18N.getString("Generic0")); | |
1275 | + } | |
1276 | + | |
1277 | + if (ptDst == null) { | |
1278 | + if (ptSrc instanceof Point2D.Double) { | |
1279 | + ptDst = new Point2D.Double(); | |
1280 | + } else { | |
1281 | + ptDst = new Point2D.Float(); | |
1282 | + } | |
1283 | + } | |
1284 | + // Copy source coords into local variables in case src == dst | |
1285 | + double x = ptSrc.getX(); | |
1286 | + double y = ptSrc.getY(); | |
1287 | + | |
1288 | + double tmp_x = (m11*m22 - m12*m21) * x + | |
1289 | + (m02*m21 - m01*m22) * y + | |
1290 | + (m01*m12 - m02*m11); | |
1291 | + double tmp_y = (m12*m20 - m10*m22) * x + | |
1292 | + (m00*m22 - m02*m20) * y + | |
1293 | + (m02*m10 - m00*m12); | |
1294 | + double w = (m10*m21 - m11*m20) * x + | |
1295 | + (m01*m20 - m00*m21) * y + | |
1296 | + (m00*m11 - m01*m10); | |
1297 | + | |
1298 | + double wabs = w; | |
1299 | + if (w < 0) { | |
1300 | + wabs = - w; | |
1301 | + } | |
1302 | + if (wabs < PERSPECTIVE_DIVIDE_EPSILON) { | |
1303 | + throw new | |
1304 | + NoninvertibleTransformException( | |
1305 | + JaiI18N.getString("PerspectiveTransform1")); | |
1306 | + } | |
1307 | + | |
1308 | + ptDst.setLocation(tmp_x/w, tmp_y/w); | |
1309 | + | |
1310 | + return ptDst; | |
1311 | + } | |
1312 | + | |
1313 | + /** | |
1314 | + * Inverse transforms an array of double precision coordinates by | |
1315 | + * this transform. | |
1316 | + * @param srcPts The array containing the source point coordinates. | |
1317 | + * Each point is stored as a pair of x,y coordinates. | |
1318 | + * @param dstPts The array where the transformed point coordinates are | |
1319 | + * returned. Each point is stored as a pair of x,y coordinates. | |
1320 | + * @param srcOff The offset to the first point to be transformed | |
1321 | + * in the source array. | |
1322 | + * @param dstOff The offset to the location where the first transformed | |
1323 | + * point is stored in the destination array. | |
1324 | + * @param numPts The number of point objects to be transformed. | |
1325 | + * @throws NoninvertibleTransformException if the matrix cannot be | |
1326 | + * inverted. | |
1327 | + * @throws IllegalArgumentException if srcPts is null | |
1328 | + * @throws ArrayIndexOutOfBoundsException if srcPts is too small | |
1329 | + * @throws NoninvertibleTransformException transform cannot be inverted | |
1330 | + */ | |
1331 | + public void inverseTransform(double[] srcPts, int srcOff, | |
1332 | + double[] dstPts, int dstOff, | |
1333 | + int numPts) | |
1334 | + throws NoninvertibleTransformException | |
1335 | + { | |
1336 | + if ( srcPts == null ) { | |
1337 | + throw new IllegalArgumentException(JaiI18N.getString("Generic0")); | |
1338 | + } | |
1339 | + | |
1340 | + if (dstPts == null) { | |
1341 | + dstPts = new double[numPts * 2 + dstOff]; | |
1342 | + } | |
1343 | + | |
1344 | + while (numPts-- > 0) { | |
1345 | + double x = srcPts[srcOff++]; | |
1346 | + double y = srcPts[srcOff++]; | |
1347 | + | |
1348 | + double tmp_x = (m11*m22 - m12*m21) * x + | |
1349 | + (m02*m21 - m01*m22) * y + | |
1350 | + (m01*m12 - m02*m11); | |
1351 | + double tmp_y = (m12*m20 - m10*m22) * x + | |
1352 | + (m00*m22 - m02*m20) * y + | |
1353 | + (m02*m10 - m00*m12); | |
1354 | + double w = (m10*m21 - m11*m20) * x + | |
1355 | + (m01*m20 - m00*m21) * y + | |
1356 | + (m00*m11 - m01*m10); | |
1357 | + | |
1358 | + double wabs = w; | |
1359 | + if (w < 0) { | |
1360 | + wabs = - w; | |
1361 | + } | |
1362 | + if (wabs < PERSPECTIVE_DIVIDE_EPSILON) { | |
1363 | + throw new NoninvertibleTransformException( | |
1364 | + JaiI18N.getString("PerspectiveTransform1")); | |
1365 | + } | |
1366 | + | |
1367 | + dstPts[dstOff++] = tmp_x / w; | |
1368 | + dstPts[dstOff++] = tmp_y / w; | |
1369 | + } | |
1370 | + } | |
1371 | + | |
1372 | + /** | |
1373 | + * Returns a String that represents the value of this Object. | |
1374 | + */ | |
1375 | + public String toString() { | |
1376 | + StringBuffer sb = new StringBuffer(); | |
1377 | + sb.append("Perspective transform matrix\n"); | |
1378 | + sb.append(this.m00); | |
1379 | + sb.append("\t"); | |
1380 | + sb.append(this.m01); | |
1381 | + sb.append("\t"); | |
1382 | + sb.append(this.m02); | |
1383 | + sb.append("\n"); | |
1384 | + sb.append(this.m10); | |
1385 | + sb.append("\t"); | |
1386 | + sb.append(this.m11); | |
1387 | + sb.append("\t"); | |
1388 | + sb.append(this.m12); | |
1389 | + sb.append("\n"); | |
1390 | + sb.append(this.m20); | |
1391 | + sb.append("\t"); | |
1392 | + sb.append(this.m21); | |
1393 | + sb.append("\t"); | |
1394 | + sb.append(this.m22); | |
1395 | + sb.append("\n"); | |
1396 | + return new String(sb); | |
1397 | + } | |
1398 | + | |
1399 | + /** | |
1400 | + * Returns the boolean true value if this PerspectiveTransform is an | |
1401 | + * identity transform. Returns false otherwise. | |
1402 | + */ | |
1403 | + public boolean isIdentity() { | |
1404 | + return m01 == 0.0 && m02 == 0.0 && | |
1405 | + m10 == 0.0 && m12 == 0.0 && | |
1406 | + m20 == 0.0 && m21 == 0.0 && | |
1407 | + m22 != 0.0 && m00/m22 == 1.0 && m11/m22 == 1.0; | |
1408 | + } | |
1409 | + | |
1410 | + /** | |
1411 | + * Returns a copy of this PerspectiveTransform object. | |
1412 | + */ | |
1413 | + public Object clone() { | |
1414 | + try { | |
1415 | + return super.clone(); | |
1416 | + } catch (CloneNotSupportedException e) { | |
1417 | + // this shouldn't happen, since we are Cloneable | |
1418 | + throw new InternalError(); | |
1419 | + } | |
1420 | + } | |
1421 | + | |
1422 | + | |
1423 | + /** | |
1424 | + * Tests if this PerspectiveTransform equals a supplied one. | |
1425 | + * | |
1426 | + * @param obj The PerspectiveTransform to be compared to this one. | |
1427 | + */ | |
1428 | + public boolean equals(Object obj) { | |
1429 | + if (!(obj instanceof PerspectiveTransform)) { | |
1430 | + return false; | |
1431 | + } | |
1432 | + | |
1433 | + PerspectiveTransform a = (PerspectiveTransform)obj; | |
1434 | + | |
1435 | + return ((m00 == a.m00) && (m10 == a.m10) && (m20 == a.m20) && | |
1436 | + (m01 == a.m01) && (m11 == a.m11) && (m21 == a.m21) && | |
1437 | + (m02 == a.m02) && (m12 == a.m12) && (m22 == a.m22)); | |
1438 | + } | |
1439 | +} | ... | ... |
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/WiimoteConnector.java
0 → 100644
... | ... | @@ -0,0 +1,51 @@ |
1 | +package org.mote.wiimote.whiteboard; | |
2 | + | |
3 | +import org.mote.wiimote.whiteboard.preferences.WWPreferences; | |
4 | + | |
5 | +import wiiremotej.WiiRemote; | |
6 | +import wiiremotej.WiiRemoteJ; | |
7 | + | |
8 | +public class WiimoteConnector { | |
9 | + | |
10 | + private WiimoteDataHandler dh; | |
11 | + | |
12 | + public WiimoteConnector(WiimoteDataHandler dh) { | |
13 | + this.dh = dh; | |
14 | + } | |
15 | + | |
16 | + public void connect() { | |
17 | + if (!WWPreferences.WIIMOTE_BT_ADDRESSES.isEmpty()) { | |
18 | + WiimoteWhiteboard.getLogger().info(String.format("Directly connecting to bluetooth address(es) %s.", WWPreferences.WIIMOTE_BT_ADDRESSES)); | |
19 | + for (int i = 0; i < Math.min(WWPreferences.WIIMOTE_BT_ADDRESSES.size(), WWPreferences.WIIMOTES); i++) { | |
20 | + connect(WWPreferences.WIIMOTE_BT_ADDRESSES.get(i)); | |
21 | + } | |
22 | + } else { | |
23 | + WiiRemoteJ.findRemotes(dh, WWPreferences.WIIMOTES); | |
24 | + } | |
25 | + } | |
26 | + | |
27 | + private void connect(final String address) { | |
28 | + new Thread(new Runnable() { | |
29 | + private boolean done = false; | |
30 | + public void run() { | |
31 | + while (!done) { | |
32 | + try { | |
33 | + WiiRemote r = WiiRemoteJ.connectToRemote(address); | |
34 | + if (r != null && r.isConnected()) { | |
35 | + done = true; | |
36 | + dh.addRemote(r); | |
37 | + } | |
38 | + } catch (Exception e) { | |
39 | + | |
40 | + try { | |
41 | + Thread.sleep(1000); | |
42 | + } catch (InterruptedException e1) { | |
43 | + e1.printStackTrace(); | |
44 | + } | |
45 | + } | |
46 | + } | |
47 | + } | |
48 | + }).start(); | |
49 | + } | |
50 | + | |
51 | +} | ... | ... |
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/WiimoteConnector.java~
0 → 100644
... | ... | @@ -0,0 +1,55 @@ |
1 | +package org.mote.wiimote.whiteboard; | |
2 | + | |
3 | +import org.mote.wiimote.whiteboard.preferences.WWPreferences; | |
4 | + | |
5 | +import wiiremotej.WiiRemote; | |
6 | +import wiiremotej.WiiRemoteJ; | |
7 | + | |
8 | +public class WiimoteConnector { | |
9 | + | |
10 | + private WiimoteDataHandler dh; | |
11 | + | |
12 | + public WiimoteConnector(WiimoteDataHandler dh) { | |
13 | + this.dh = dh; | |
14 | + } | |
15 | + | |
16 | + public void connect() { | |
17 | + if (!WWPreferences.WIIMOTE_BT_ADDRESSES.isEmpty()) { | |
18 | + WiimoteWhiteboard.getLogger().info(String.format("Directly connecting to bluetooth address(es) %s.", WWPreferences.WIIMOTE_BT_ADDRESSES)); | |
19 | + for (int i = 0; i < Math.min(WWPreferences.WIIMOTE_BT_ADDRESSES.size(), WWPreferences.WIIMOTES); i++) { | |
20 | + connect(WWPreferences.WIIMOTE_BT_ADDRESSES.get(i)); | |
21 | + } | |
22 | + } else { | |
23 | + WiiRemoteJ.findRemotes(dh, WWPreferences.WIIMOTES); | |
24 | + } | |
25 | + } | |
26 | + | |
27 | + private void connect(final String address) { | |
28 | + new Thread(new Runnable() { | |
29 | + private boolean done = false; | |
30 | + public void run() { | |
31 | + while (!done) { | |
32 | + try { | |
33 | + WiiRemote r = WiiRemoteJ.connectToRemote(address); | |
34 | + if (r != null && r.isConnected()) { | |
35 | + done = true; | |
36 | + dh.addRemote(r); | |
37 | + } | |
38 | + } catch (Exception e) { | |
39 | +// if (e.getCause().getMessage().startsWith("Failed to open baseband connection")) { | |
40 | +// // apparently simple timeout error | |
41 | +// } else { | |
42 | +// // "regular" connection error | |
43 | +// } | |
44 | + try { | |
45 | + Thread.sleep(1000); | |
46 | + } catch (InterruptedException e1) { | |
47 | + e1.printStackTrace(); | |
48 | + } | |
49 | + } | |
50 | + } | |
51 | + } | |
52 | + }).start(); | |
53 | + } | |
54 | + | |
55 | +} | ... | ... |
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/WiimoteDataHandler.java
0 → 100644
... | ... | @@ -0,0 +1,314 @@ |
1 | +package org.mote.wiimote.whiteboard; | |
2 | + | |
3 | +import java.awt.Point; | |
4 | +import java.util.Collection; | |
5 | +import java.util.Collections; | |
6 | +import java.util.EventObject; | |
7 | +import java.util.HashSet; | |
8 | +import java.util.LinkedHashMap; | |
9 | +import java.util.Map; | |
10 | +import java.util.Set; | |
11 | +import java.util.logging.Level; | |
12 | + | |
13 | +import org.jdesktop.application.Application; | |
14 | +import org.jdesktop.application.Application.ExitListener; | |
15 | +import org.mote.wiimote.whiteboard.calibration.WiimoteCalibration; | |
16 | +import org.mote.wiimote.whiteboard.ds.IRDot; | |
17 | +import org.mote.wiimote.whiteboard.ds.Wiimote; | |
18 | +import org.mote.wiimote.whiteboard.mouse.CursorControlStrategy; | |
19 | +import org.mote.wiimote.whiteboard.mouse.Mouse; | |
20 | +import org.mote.wiimote.whiteboard.mouse.smoothing.MouseSmoothingStrategy; | |
21 | +import org.mote.wiimote.whiteboard.preferences.WWPreferences; | |
22 | +import org.mote.wiimote.whiteboard.preferences.WWPreferences.PreferencesListener; | |
23 | + | |
24 | +import wiiremotej.WiiRemote; | |
25 | +import wiiremotej.WiiRemoteJ; | |
26 | +import wiiremotej.event.WRButtonEvent; | |
27 | +import wiiremotej.event.WRIREvent; | |
28 | +import wiiremotej.event.WRStatusEvent; | |
29 | +import wiiremotej.event.WiiDeviceDiscoveredEvent; | |
30 | +import wiiremotej.event.WiiDeviceDiscoveryListener; | |
31 | +import wiiremotej.event.WiiRemoteAdapter; | |
32 | + | |
33 | +public class WiimoteDataHandler extends WiiRemoteAdapter implements ExitListener, WiiDeviceDiscoveryListener, PreferencesListener { | |
34 | + | |
35 | + public static interface WiimoteDataListener { | |
36 | + public void wiimoteConnected(Wiimote wiimote); | |
37 | + public void wiimoteDisconnected(Wiimote wiimote); | |
38 | + public void irLights(Wiimote wiimote, IRDot[] lights); | |
39 | + public void irWarped(Map<Wiimote, IRDot[]> data, Point[] warped); | |
40 | + public void batteryLevel(Wiimote wiimote, double level); | |
41 | + } | |
42 | + | |
43 | + private Map<WiiRemote, Wiimote> remotes = new LinkedHashMap<WiiRemote, Wiimote>(WWPreferences.WIIMOTES, 1f); | |
44 | + private Map<WiiRemote, WRIREvent> events = new LinkedHashMap<WiiRemote, WRIREvent>(WWPreferences.WIIMOTES, 1f); | |
45 | + private final WiimoteCalibration calibration; | |
46 | + private static final WWPreferences prefs = WWPreferences.getPreferences(); | |
47 | + private final Set<WiimoteDataListener> listener = Collections.synchronizedSet(new HashSet<WiimoteDataListener>()); | |
48 | + | |
49 | + private boolean cursorControl = true; | |
50 | + | |
51 | + private MouseSmoothingStrategy mss[] = new MouseSmoothingStrategy[4]; | |
52 | + private CursorControlStrategy cursorControlStrategy; | |
53 | + | |
54 | + public WiimoteDataHandler(WiimoteCalibration calibration) { | |
55 | + this.calibration = calibration; | |
56 | + Application.getInstance().addExitListener(this); | |
57 | + prefs.addPreferencesListener(this); | |
58 | + preferencesChanged(); | |
59 | + new WiimoteConnector(this).connect(); | |
60 | + } | |
61 | + | |
62 | + public void enableIR(Wiimote wiimote) throws Exception { | |
63 | + if (wiimote != null && wiimote.getWiiRemote().isConnected()) { | |
64 | + wiimote.getWiiRemote().setIRSensorEnabled(true, WRIREvent.BASIC, WWPreferences.SENSITIVITY_SETTINGS); | |
65 | + } | |
66 | + WiimoteWhiteboard.getLogger().info(String.format("(Re-)Setting IR sensor of Wiimote %d: %s", wiimote.getId(), (wiimote != null && wiimote.getWiiRemote().isConnected() ? "done" : "not connected"))); | |
67 | + } | |
68 | + | |
69 | + void addRemote(final WiiRemote remote) { | |
70 | + try { | |
71 | + int id = remotes.size()+1; | |
72 | + final Wiimote wiimote = new Wiimote(remote, remote.getBluetoothAddress(), id); | |
73 | + remotes.put(remote, wiimote); | |
74 | + remote.setAccelerometerEnabled(false); | |
75 | + enableIR(wiimote); | |
76 | + remote.setLEDIlluminated(id-1, true); | |
77 | + remote.setUseMouse(false); | |
78 | + | |
79 | + synchronized (listener) { | |
80 | + for (WiimoteDataListener l : listener) | |
81 | + l.wiimoteConnected(wiimote); | |
82 | + } | |
83 | + | |
84 | + } catch (Exception e) { | |
85 | + e.printStackTrace(); | |
86 | + WiimoteWhiteboard.getLogger().log(Level.SEVERE, "Error on configuring Wii Remote", e); | |
87 | + } | |
88 | + | |
89 | + remote.addWiiRemoteListener(this); | |
90 | + | |
91 | + // update battery level every minute | |
92 | + new Thread(new Runnable() { | |
93 | + public void run() { | |
94 | + while (true) { | |
95 | + try { | |
96 | + // triggers #statusReported(WRStatusEvent) | |
97 | + if (remote.isConnected()) | |
98 | + remote.requestStatus(); | |
99 | + } catch (Exception e) { | |
100 | + e.printStackTrace(); | |
101 | + WiimoteWhiteboard.getLogger().log(Level.WARNING, "Error on requesting status from Wii Remote", e); | |
102 | + } | |
103 | + try { | |
104 | + Thread.sleep(60 * 1000); | |
105 | + } catch (InterruptedException e) { | |
106 | + e.printStackTrace(); | |
107 | + } | |
108 | + } | |
109 | + } | |
110 | + }).start(); | |
111 | + } | |
112 | + | |
113 | + | |
114 | + /* | |
115 | + * EXIT | |
116 | + */ | |
117 | + | |
118 | + public void willExit(EventObject event) { | |
119 | + for (WiiRemote remote : remotes.keySet()) | |
120 | + remote.disconnect(); | |
121 | + } | |
122 | + | |
123 | + public boolean canExit(EventObject event) { | |
124 | + return true; | |
125 | + } | |
126 | + | |
127 | + | |
128 | + /* | |
129 | + * WIIREMOTEJ LISTENER | |
130 | + */ | |
131 | + | |
132 | + @Override | |
133 | + public synchronized void IRInputReceived(WRIREvent e) { | |
134 | + events.put(e.getSource(), e); | |
135 | + // wait till data from all connected wiimotes was received once and only process input on data from first wiimote to reduce the number of times the function gets called | |
136 | + if (events.size() == getNumberOfConnectedWiimotes() && remotes.get(e.getSource()).getId() == 1) | |
137 | + IRInputReceived(); | |
138 | + } | |
139 | + | |
140 | + private void IRInputReceived() { | |
141 | + boolean firstDotVisible = false; | |
142 | + Map<Wiimote, IRDot[]> data = new LinkedHashMap<Wiimote, IRDot[]>(); | |
143 | + for (WiiRemote r : events.keySet()) { | |
144 | + Wiimote wiimote = remotes.get(r); | |
145 | + IRDot[] dots = IRDot.getIRDots(events.get(r).getIRLights()); | |
146 | + | |
147 | + synchronized (listener) { | |
148 | + for (WiimoteDataListener l : listener) | |
149 | + l.irLights(wiimote, dots); | |
150 | + } | |
151 | + | |
152 | + // exclude points from uncalibrated wiimotes during "normal operation" | |
153 | + if (!calibration.isDone() || calibration.isCalibrated(wiimote)) { | |
154 | + firstDotVisible = firstDotVisible || dots[0] != null; | |
155 | + data.put(wiimote, dots); | |
156 | + } | |
157 | + } | |
158 | + | |
159 | + if (calibration.isDone()) { | |
160 | + // should always be true, but just in case... | |
161 | + if (calibration.isAnyCalibrated(data.keySet())) { | |
162 | + Point warped[] = calibration.warp(data); | |
163 | + for (int i = 0; i < 4; i++) { | |
164 | + if (warped[i] != null) { | |
165 | + warped[i] = mss[i].translate(warped[i]); | |
166 | + } else { | |
167 | + mss[i].reset(); | |
168 | + } | |
169 | + } | |
170 | + | |
171 | + if (isCursorControl()) { | |
172 | + cursorControlStrategy.process(warped[0]); | |
173 | + } else { | |
174 | +// if (Mouse.LEFT_BUTTON.isPressed()) | |
175 | + Mouse.LEFT_BUTTON.setPressed(false); | |
176 | +// if (Mouse.RIGHT_BUTTON.isPressed()) | |
177 | + Mouse.RIGHT_BUTTON.setPressed(false); | |
178 | + } | |
179 | + synchronized (listener) { | |
180 | + for (WiimoteDataListener l : listener) | |
181 | + l.irWarped(data, warped); | |
182 | + } | |
183 | + | |
184 | + } | |
185 | + } else if (calibration.inProgress()) { | |
186 | + if (firstDotVisible) | |
187 | + calibration.process(data); | |
188 | + } | |
189 | + | |
190 | + } | |
191 | + | |
192 | + @Override | |
193 | + public void statusReported(WRStatusEvent e) { | |
194 | + synchronized (listener) { | |
195 | + for (WiimoteDataListener l : listener) | |
196 | + l.batteryLevel(remotes.get(e.getSource()), e.getBatteryLevel()); | |
197 | + } | |
198 | + } | |
199 | + | |
200 | + @Override | |
201 | + public void buttonInputReceived(WRButtonEvent e) { | |
202 | + if (e.isOnlyPressed(WRButtonEvent.A)) { | |
203 | + calibration.start(getConnectedWiimotes()); | |
204 | + } else if (e.isOnlyPressed(WRButtonEvent.HOME)) { | |
205 | + Application.getInstance().exit(); | |
206 | + } | |
207 | + } | |
208 | + | |
209 | + public void wiiDeviceDiscovered(WiiDeviceDiscoveredEvent e) { | |
210 | + if (e.getWiiDevice() instanceof WiiRemote) { | |
211 | + addRemote((WiiRemote)e.getWiiDevice()); | |
212 | + } | |
213 | + } | |
214 | + | |
215 | + public void findFinished(int numberFound) { | |
216 | + } | |
217 | + | |
218 | + @Override | |
219 | + public void disconnected() { | |
220 | + WiiRemoteJ.stopFind(); | |
221 | + WiiRemote remove = null; | |
222 | + for (WiiRemote remote : remotes.keySet()) { | |
223 | + if (!remote.isConnected()) { | |
224 | + remove = remote; | |
225 | + synchronized (listener) { | |
226 | + for (WiimoteDataListener l : listener) | |
227 | + l.wiimoteDisconnected(remotes.get(remote)); | |
228 | + } | |
229 | + break; | |
230 | + } | |
231 | + } | |
232 | + if (remove != null) remotes.remove(remove); | |
233 | + } | |
234 | + | |
235 | + | |
236 | + /* | |
237 | + * LISTENER | |
238 | + */ | |
239 | + | |
240 | + public void addWiimoteDataListener(WiimoteDataListener l) { | |
241 | + listener.add(l); | |
242 | + } | |
243 | + | |
244 | + public void removeWiimoteDataListener(WiimoteDataListener l) { | |
245 | + listener.remove(l); | |
246 | + } | |
247 | + | |
248 | + /* | |
249 | + * PREFERENCES STUFF | |
250 | + */ | |
251 | + | |
252 | + public void preferencesChanged() { | |
253 | + updateMSS(); | |
254 | + updateCursorControlStrategy(); | |
255 | + | |
256 | + @SuppressWarnings("unchecked") | |
257 | + private void updateCursorControlStrategy() { | |
258 | + if (cursorControlStrategy == null || !cursorControlStrategy.getClass().getName().equals(prefs.getCursorControl())) { | |
259 | + try { | |
260 | + Class<?> c = Class.forName(prefs.getCursorControl()); | |
261 | + cursorControlStrategy = ((Class<? extends CursorControlStrategy>)c).newInstance(); | |
262 | + } catch (Exception e) { | |
263 | + e.printStackTrace(); | |
264 | + WiimoteWhiteboard.getLogger().log(Level.SEVERE, "Cursor Control Method error.", e); | |
265 | + } | |
266 | + } | |
267 | + } | |
268 | + | |
269 | + @SuppressWarnings("unchecked") | |
270 | + private void updateMSS() { | |
271 | + if (mss[0] == null || !mss[0].getClass().getName().equals(prefs.getMouseSmoothing())) { | |
272 | + try { | |
273 | + Class<?> c = Class.forName(prefs.getMouseSmoothing()); | |
274 | + for (int i = 0; i < 4; i++) | |
275 | + mss[i] = ((Class<? extends MouseSmoothingStrategy>)c).newInstance(); | |
276 | + } catch (Exception e) { | |
277 | + e.printStackTrace(); | |
278 | + WiimoteWhiteboard.getLogger().log(Level.SEVERE, "Mouse Movement Smoothing error.", e); | |
279 | + } | |
280 | + } | |
281 | + } | |
282 | + | |
283 | + /* | |
284 | + * GETTER & SETTER | |
285 | + */ | |
286 | + | |
287 | + public boolean isConnected() { | |
288 | + for (WiiRemote remote : remotes.keySet()) { | |
289 | + if (remote.isConnected()) return true; | |
290 | + } | |
291 | + return false; | |
292 | + } | |
293 | + | |
294 | + public boolean isConnected(Wiimote wiimote) { | |
295 | + return wiimote != null && wiimote.getWiiRemote().isConnected(); | |
296 | + } | |
297 | + | |
298 | + public int getNumberOfConnectedWiimotes() { | |
299 | + return remotes.size(); | |
300 | + } | |
301 | + | |
302 | + public Collection<Wiimote> getConnectedWiimotes() { | |
303 | + return remotes.values(); | |
304 | + } | |
305 | + | |
306 | + public boolean isCursorControl() { | |
307 | + return cursorControl; | |
308 | + } | |
309 | + | |
310 | + public void setCursorControl(boolean cursorControl) { | |
311 | + this.cursorControl = cursorControl; | |
312 | + } | |
313 | + | |
314 | +} | ... | ... |
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/WiimoteDataHandler.java~
0 → 100644
... | ... | @@ -0,0 +1,339 @@ |
1 | +package org.mote.wiimote.whiteboard; | |
2 | + | |
3 | +import java.awt.Point; | |
4 | +import java.util.Collection; | |
5 | +import java.util.Collections; | |
6 | +import java.util.EventObject; | |
7 | +import java.util.HashSet; | |
8 | +import java.util.LinkedHashMap; | |
9 | +import java.util.Map; | |
10 | +import java.util.Set; | |
11 | +import java.util.logging.Level; | |
12 | + | |
13 | +import org.jdesktop.application.Application; | |
14 | +import org.jdesktop.application.Application.ExitListener; | |
15 | +import org.mote.wiimote.whiteboard.calibration.WiimoteCalibration; | |
16 | +import org.mote.wiimote.whiteboard.ds.IRDot; | |
17 | +import org.mote.wiimote.whiteboard.ds.Wiimote; | |
18 | +import org.mote.wiimote.whiteboard.mouse.CursorControlStrategy; | |
19 | +import org.mote.wiimote.whiteboard.mouse.Mouse; | |
20 | +import org.mote.wiimote.whiteboard.mouse.smoothing.MouseSmoothingStrategy; | |
21 | +import org.mote.wiimote.whiteboard.preferences.WWPreferences; | |
22 | +import org.mote.wiimote.whiteboard.preferences.WWPreferences.PreferencesListener; | |
23 | + | |
24 | +import wiiremotej.WiiRemote; | |
25 | +import wiiremotej.WiiRemoteJ; | |
26 | +import wiiremotej.event.WRButtonEvent; | |
27 | +import wiiremotej.event.WRIREvent; | |
28 | +import wiiremotej.event.WRStatusEvent; | |
29 | +import wiiremotej.event.WiiDeviceDiscoveredEvent; | |
30 | +import wiiremotej.event.WiiDeviceDiscoveryListener; | |
31 | +import wiiremotej.event.WiiRemoteAdapter; | |
32 | + | |
33 | +public class WiimoteDataHandler extends WiiRemoteAdapter implements ExitListener, WiiDeviceDiscoveryListener, PreferencesListener { | |
34 | + | |
35 | + public static interface WiimoteDataListener { | |
36 | + public void wiimoteConnected(Wiimote wiimote); | |
37 | + public void wiimoteDisconnected(Wiimote wiimote); | |
38 | + public void irLights(Wiimote wiimote, IRDot[] lights); | |
39 | + public void irWarped(Map<Wiimote, IRDot[]> data, Point[] warped); | |
40 | + public void batteryLevel(Wiimote wiimote, double level); | |
41 | + } | |
42 | + | |
43 | + private Map<WiiRemote, Wiimote> remotes = new LinkedHashMap<WiiRemote, Wiimote>(WWPreferences.WIIMOTES, 1f); | |
44 | + private Map<WiiRemote, WRIREvent> events = new LinkedHashMap<WiiRemote, WRIREvent>(WWPreferences.WIIMOTES, 1f); | |
45 | + private final WiimoteCalibration calibration; | |
46 | + private static final WWPreferences prefs = WWPreferences.getPreferences(); | |
47 | + private final Set<WiimoteDataListener> listener = Collections.synchronizedSet(new HashSet<WiimoteDataListener>()); | |
48 | + | |
49 | + private boolean cursorControl = true; | |
50 | + | |
51 | + private MouseSmoothingStrategy mss[] = new MouseSmoothingStrategy[4]; | |
52 | + private CursorControlStrategy cursorControlStrategy; | |
53 | + | |
54 | + public WiimoteDataHandler(WiimoteCalibration calibration) { | |
55 | + this.calibration = calibration; | |
56 | + Application.getInstance().addExitListener(this); | |
57 | + prefs.addPreferencesListener(this); | |
58 | + preferencesChanged(); | |
59 | + new WiimoteConnector(this).connect(); | |
60 | + } | |
61 | + | |
62 | + public void enableIR(Wiimote wiimote) throws Exception { | |
63 | + if (wiimote != null && wiimote.getWiiRemote().isConnected()) { | |
64 | + wiimote.getWiiRemote().setIRSensorEnabled(true, WRIREvent.BASIC, WWPreferences.SENSITIVITY_SETTINGS); | |
65 | + } | |
66 | + WiimoteWhiteboard.getLogger().info(String.format("(Re-)Setting IR sensor of Wiimote %d: %s", wiimote.getId(), (wiimote != null && wiimote.getWiiRemote().isConnected() ? "done" : "not connected"))); | |
67 | + } | |
68 | + | |
69 | + void addRemote(final WiiRemote remote) { | |
70 | + try { | |
71 | + int id = remotes.size()+1; | |
72 | + final Wiimote wiimote = new Wiimote(remote, remote.getBluetoothAddress(), id); | |
73 | + remotes.put(remote, wiimote); | |
74 | + remote.setAccelerometerEnabled(false); | |
75 | + enableIR(wiimote); | |
76 | + remote.setLEDIlluminated(id-1, true); | |
77 | + remote.setUseMouse(false); | |
78 | + | |
79 | + synchronized (listener) { | |
80 | + for (WiimoteDataListener l : listener) | |
81 | + l.wiimoteConnected(wiimote); | |
82 | + } | |
83 | + | |
84 | + } catch (Exception e) { | |
85 | + e.printStackTrace(); | |
86 | + WiimoteWhiteboard.getLogger().log(Level.SEVERE, "Error on configuring Wii Remote", e); | |
87 | + } | |
88 | + | |
89 | + remote.addWiiRemoteListener(this); | |
90 | + | |
91 | + // update battery level every minute | |
92 | + new Thread(new Runnable() { | |
93 | + public void run() { | |
94 | + while (true) { | |
95 | + try { | |
96 | + // triggers #statusReported(WRStatusEvent) | |
97 | + if (remote.isConnected()) | |
98 | + remote.requestStatus(); | |
99 | + } catch (Exception e) { | |
100 | + e.printStackTrace(); | |
101 | + WiimoteWhiteboard.getLogger().log(Level.WARNING, "Error on requesting status from Wii Remote", e); | |
102 | + } | |
103 | + try { | |
104 | + Thread.sleep(60 * 1000); | |
105 | + } catch (InterruptedException e) { | |
106 | + e.printStackTrace(); | |
107 | + } | |
108 | + } | |
109 | + } | |
110 | + }).start(); | |
111 | + } | |
112 | + | |
113 | + | |
114 | + /* | |
115 | + * EXIT | |
116 | + */ | |
117 | + | |
118 | + public void willExit(EventObject event) { | |
119 | + for (WiiRemote remote : remotes.keySet()) | |
120 | + remote.disconnect(); | |
121 | + } | |
122 | + | |
123 | + public boolean canExit(EventObject event) { | |
124 | + return true; | |
125 | + } | |
126 | + | |
127 | + | |
128 | + /* | |
129 | + * WIIREMOTEJ LISTENER | |
130 | + */ | |
131 | + | |
132 | + @Override | |
133 | + public synchronized void IRInputReceived(WRIREvent e) { | |
134 | + events.put(e.getSource(), e); | |
135 | + // wait till data from all connected wiimotes was received once and only process input on data from first wiimote to reduce the number of times the function gets called | |
136 | + if (events.size() == getNumberOfConnectedWiimotes() && remotes.get(e.getSource()).getId() == 1) | |
137 | + IRInputReceived(); | |
138 | + } | |
139 | + | |
140 | + private void IRInputReceived() { | |
141 | + boolean firstDotVisible = false; | |
142 | + Map<Wiimote, IRDot[]> data = new LinkedHashMap<Wiimote, IRDot[]>(); | |
143 | + for (WiiRemote r : events.keySet()) { | |
144 | + Wiimote wiimote = remotes.get(r); | |
145 | + IRDot[] dots = IRDot.getIRDots(events.get(r).getIRLights()); | |
146 | + | |
147 | + synchronized (listener) { | |
148 | + for (WiimoteDataListener l : listener) | |
149 | + l.irLights(wiimote, dots); | |
150 | + } | |
151 | + | |
152 | + // exclude points from uncalibrated wiimotes during "normal operation" | |
153 | + if (!calibration.isDone() || calibration.isCalibrated(wiimote)) { | |
154 | + firstDotVisible = firstDotVisible || dots[0] != null; | |
155 | + data.put(wiimote, dots); | |
156 | + } | |
157 | + } | |
158 | + | |
159 | + if (calibration.isDone()) { | |
160 | + // should always be true, but just in case... | |
161 | + if (calibration.isAnyCalibrated(data.keySet())) { | |
162 | + Point warped[] = calibration.warp(data); | |
163 | + for (int i = 0; i < 4; i++) { | |
164 | + if (warped[i] != null) { | |
165 | + warped[i] = mss[i].translate(warped[i]); | |
166 | + } else { | |
167 | + mss[i].reset(); | |
168 | + } | |
169 | + } | |
170 | + | |
171 | + if (isCursorControl()) { | |
172 | + cursorControlStrategy.process(warped[0]); | |
173 | + } else { | |
174 | +// if (Mouse.LEFT_BUTTON.isPressed()) | |
175 | + Mouse.LEFT_BUTTON.setPressed(false); | |
176 | +// if (Mouse.RIGHT_BUTTON.isPressed()) | |
177 | + Mouse.RIGHT_BUTTON.setPressed(false); | |
178 | + } | |
179 | + | |
180 | +// if (warped[0] != null) { | |
181 | +// // normal operation after calibration has been done | |
182 | +// if (isCursorControl()) { | |
183 | +// Mouse.move(warped[0]); | |
184 | +// rcs.process(warped[0]); | |
185 | +// if (prefs.isLeftClick() && !(prefs.isRightClick() && rcs.trigger())) { | |
186 | +// Mouse.LEFT_BUTTON.setPressed(true); | |
187 | +// } | |
188 | +// } | |
189 | +// } else { | |
190 | +// rcs.process(null); | |
191 | +// Mouse.LEFT_BUTTON.setPressed(false); | |
192 | +// } | |
193 | + | |
194 | + synchronized (listener) { | |
195 | + for (WiimoteDataListener l : listener) | |
196 | + l.irWarped(data, warped); | |
197 | + } | |
198 | + | |
199 | + } | |
200 | + } else if (calibration.inProgress()) { | |
201 | + if (firstDotVisible) | |
202 | + calibration.process(data); | |
203 | + } | |
204 | + | |
205 | + } | |
206 | + | |
207 | + @Override | |
208 | + public void statusReported(WRStatusEvent e) { | |
209 | + synchronized (listener) { | |
210 | + for (WiimoteDataListener l : listener) | |
211 | + l.batteryLevel(remotes.get(e.getSource()), e.getBatteryLevel()); | |
212 | + } | |
213 | + } | |
214 | + | |
215 | + @Override | |
216 | + public void buttonInputReceived(WRButtonEvent e) { | |
217 | + if (e.isOnlyPressed(WRButtonEvent.A)) { | |
218 | + calibration.start(getConnectedWiimotes()); | |
219 | + } else if (e.isOnlyPressed(WRButtonEvent.HOME)) { | |
220 | + Application.getInstance().exit(); | |
221 | + } | |
222 | + } | |
223 | + | |
224 | + public void wiiDeviceDiscovered(WiiDeviceDiscoveredEvent e) { | |
225 | + if (e.getWiiDevice() instanceof WiiRemote) { | |
226 | + addRemote((WiiRemote)e.getWiiDevice()); | |
227 | + } | |
228 | + } | |
229 | + | |
230 | + public void findFinished(int numberFound) { | |
231 | + } | |
232 | + | |
233 | + @Override | |
234 | + public void disconnected() { | |
235 | + // TODO support dis-/reconnecting? | |
236 | + WiiRemoteJ.stopFind(); | |
237 | + WiiRemote remove = null; | |
238 | + for (WiiRemote remote : remotes.keySet()) { | |
239 | + if (!remote.isConnected()) { | |
240 | + remove = remote; | |
241 | + synchronized (listener) { | |
242 | + for (WiimoteDataListener l : listener) | |
243 | + l.wiimoteDisconnected(remotes.get(remote)); | |
244 | + } | |
245 | + break; | |
246 | + } | |
247 | + } | |
248 | + if (remove != null) remotes.remove(remove); | |
249 | + } | |
250 | + | |
251 | + | |
252 | + /* | |
253 | + * LISTENER | |
254 | + */ | |
255 | + | |
256 | + public void addWiimoteDataListener(WiimoteDataListener l) { | |
257 | + listener.add(l); | |
258 | + } | |
259 | + | |
260 | + public void removeWiimoteDataListener(WiimoteDataListener l) { | |
261 | + listener.remove(l); | |
262 | + } | |
263 | + | |
264 | + /* | |
265 | + * PREFERENCES STUFF | |
266 | + */ | |
267 | + | |
268 | + public void preferencesChanged() { | |
269 | + updateMSS(); | |
270 | + updateCursorControlStrategy(); | |
271 | +// final int newNumWiimotes = prefs.getNumberOfWiimotes(); | |
272 | +// if (numWiimotes != newNumWiimotes) { | |
273 | +// WiiRemoteJ.stopFind(); | |
274 | +// if (newNumWiimotes > getNumberOfConnectedWiimotes()) { | |
275 | +// WiiRemoteJ.findRemotes(this, newNumWiimotes - getNumberOfConnectedWiimotes()); | |
276 | +// } | |
277 | +// numWiimotes = newNumWiimotes; | |
278 | +// } | |
279 | + } | |
280 | + | |
281 | + @SuppressWarnings("unchecked") | |
282 | + private void updateCursorControlStrategy() { | |
283 | + if (cursorControlStrategy == null || !cursorControlStrategy.getClass().getName().equals(prefs.getCursorControl())) { | |
284 | + try { | |
285 | + Class<?> c = Class.forName(prefs.getCursorControl()); | |
286 | + cursorControlStrategy = ((Class<? extends CursorControlStrategy>)c).newInstance(); | |
287 | + } catch (Exception e) { | |
288 | + e.printStackTrace(); | |
289 | + WiimoteWhiteboard.getLogger().log(Level.SEVERE, "Cursor Control Method error.", e); | |
290 | + } | |
291 | + } | |
292 | + } | |
293 | + | |
294 | + @SuppressWarnings("unchecked") | |
295 | + private void updateMSS() { | |
296 | + if (mss[0] == null || !mss[0].getClass().getName().equals(prefs.getMouseSmoothing())) { | |
297 | + try { | |
298 | + Class<?> c = Class.forName(prefs.getMouseSmoothing()); | |
299 | + for (int i = 0; i < 4; i++) | |
300 | + mss[i] = ((Class<? extends MouseSmoothingStrategy>)c).newInstance(); | |
301 | + } catch (Exception e) { | |
302 | + e.printStackTrace(); | |
303 | + WiimoteWhiteboard.getLogger().log(Level.SEVERE, "Mouse Movement Smoothing error.", e); | |
304 | + } | |
305 | + } | |
306 | + } | |
307 | + | |
308 | + /* | |
309 | + * GETTER & SETTER | |
310 | + */ | |
311 | + | |
312 | + public boolean isConnected() { | |
313 | + for (WiiRemote remote : remotes.keySet()) { | |
314 | + if (remote.isConnected()) return true; | |
315 | + } | |
316 | + return false; | |
317 | + } | |
318 | + | |
319 | + public boolean isConnected(Wiimote wiimote) { | |
320 | + return wiimote != null && wiimote.getWiiRemote().isConnected(); | |
321 | + } | |
322 | + | |
323 | + public int getNumberOfConnectedWiimotes() { | |
324 | + return remotes.size(); | |
325 | + } | |
326 | + | |
327 | + public Collection<Wiimote> getConnectedWiimotes() { | |
328 | + return remotes.values(); | |
329 | + } | |
330 | + | |
331 | + public boolean isCursorControl() { | |
332 | + return cursorControl; | |
333 | + } | |
334 | + | |
335 | + public void setCursorControl(boolean cursorControl) { | |
336 | + this.cursorControl = cursorControl; | |
337 | + } | |
338 | + | |
339 | +} | ... | ... |
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/WiimoteWhiteboard.java
0 → 100644
... | ... | @@ -0,0 +1,153 @@ |
1 | +package org.mote.wiimote.whiteboard; | |
2 | + | |
3 | +import java.util.Locale; | |
4 | +import java.util.logging.Level; | |
5 | +import java.util.logging.Logger; | |
6 | + | |
7 | +import javax.swing.JFrame; | |
8 | +import javax.swing.JOptionPane; | |
9 | + | |
10 | +import org.jdesktop.application.Action; | |
11 | +import org.jdesktop.application.Application; | |
12 | +import org.jdesktop.application.SingleFrameApplication; | |
13 | +import org.mote.wiimote.whiteboard.calibration.CalibrationPersistence; | |
14 | +import org.mote.wiimote.whiteboard.calibration.WiimoteCalibration; | |
15 | +import org.mote.wiimote.whiteboard.calibration.WiimoteCalibration.CalibrationEvent; | |
16 | +import org.mote.wiimote.whiteboard.gui.AboutWindow; | |
17 | +import org.mote.wiimote.whiteboard.gui.HelpHandler; | |
18 | +import org.mote.wiimote.whiteboard.gui.LogWindow; | |
19 | +import org.mote.wiimote.whiteboard.gui.MainPanel; | |
20 | +import org.mote.wiimote.whiteboard.gui.MenuBar; | |
21 | +import org.mote.wiimote.whiteboard.gui.PreferencesWindow; | |
22 | +import org.mote.wiimote.whiteboard.mouse.Mouse; | |
23 | +import org.mote.wiimote.whiteboard.preferences.WWPreferences; | |
24 | +import org.mote.wiimote.whiteboard.tuio.TuioTransmitter; | |
25 | +import org.mote.wiimote.whiteboard.util.BareBonesBrowserLaunch; | |
26 | +import org.mote.wiimote.whiteboard.util.UpdateNotifier; | |
27 | +import org.mote.wiimote.whiteboard.util.Util; | |
28 | +import org.mote.wiimote.whiteboard.util.WiiRemoteJErrorHandler; | |
29 | + | |
30 | +import wiiremotej.WiiRemoteJ; | |
31 | +import apple.dts.samplecode.osxadapter.OSXAdapter; | |
32 | + | |
33 | +public class WiimoteWhiteboard extends SingleFrameApplication { | |
34 | + | |
35 | + public static void main(String args[]) { | |
36 | + if (Util.MAC_OS_X && !Util.INSIDE_APP_BUNDLE) { | |
37 | + System.setProperty("apple.laf.useScreenMenuBar", "true"); | |
38 | + System.setProperty("com.apple.mrj.application.apple.menu.about.name", getProperty("id")); | |
39 | + } | |
40 | + System.setProperty("bluecove.jsr82.psm_minimum_off", "true"); | |
41 | + | |
42 | + String lang = WWPreferences.getPreferences().getLanguage(); | |
43 | + if (lang.length() > 0) { | |
44 | + String[] langParts = lang.split("_"); | |
45 | + switch (langParts.length) { | |
46 | + case 1: | |
47 | + Locale.setDefault(new Locale(langParts[0])); | |
48 | + break; | |
49 | + case 2: | |
50 | + Locale.setDefault(new Locale(langParts[0], langParts[1])); | |
51 | + break; | |
52 | + } | |
53 | + } | |
54 | + Application.launch(WiimoteWhiteboard.class, args); | |
55 | + } | |
56 | + | |
57 | + @Override | |
58 | + protected void startup() { | |
59 | + try { | |
60 | + new Thread(new Runnable() { | |
61 | + public void run() { | |
62 | + // blocking | |
63 | + if (WWPreferences.getPreferences().checkForUpdates()) | |
64 | + UpdateNotifier.checkForUpdate(getProperty("version")); | |
65 | + } | |
66 | + }).start(); | |
67 | + | |
68 | + Logger.getLogger("wiimotewhiteboard").setUseParentHandlers(false); | |
69 | + | |
70 | + final JFrame f = getMainFrame(); | |
71 | + LogWindow lw = new LogWindow(); | |
72 | + | |
73 | + final WiimoteCalibration calibration = new WiimoteCalibration(); | |
74 | + WiimoteDataHandler dh = new WiimoteDataHandler(calibration); | |
75 | + | |
76 | +// new IRDotLogger(dh); | |
77 | + | |
78 | + MainPanel mp = new MainPanel(dh, calibration); | |
79 | + AboutWindow af = new AboutWindow(); | |
80 | + HelpHandler hh = new HelpHandler(); | |
81 | + PreferencesWindow pf = new PreferencesWindow(mp, hh); | |
82 | + f.setJMenuBar(new MenuBar(pf, af, hh, lw)); | |
83 | + registerForMacOSXEvents(pf, af); | |
84 | + | |
85 | + // update Mouse's screen | |
86 | + calibration.addCalibrationEventListener(new WiimoteCalibration.CalibrationEventListener() { | |
87 | + public void calibrationEvent(CalibrationEvent e) { | |
88 | + if (e == CalibrationEvent.SCREEN_CHANGED) | |
89 | + Mouse.setScreen(calibration.getScreen()); | |
90 | + } | |
91 | + }); | |
92 | + | |
93 | + new CalibrationPersistence(calibration); | |
94 | + calibration.setScreen(WiimoteCalibration.DEFAULT_SCREEN); | |
95 | + | |
96 | + new TuioTransmitter(dh, calibration); | |
97 | + | |
98 | + WiiRemoteJ.setConsoleLoggingErrors(); | |
99 | + Logger.getLogger("wiiremotej").setLevel(Level.ALL); | |
100 | + Logger.getLogger("wiiremotej").addHandler(new WiiRemoteJErrorHandler(dh)); | |
101 | + | |
102 | + try { | |
103 | + getContext().getSessionStorage().restore(f, "mainFrame.session.xml"); | |
104 | + } catch (Exception e) {} | |
105 | + | |
106 | + show(mp); | |
107 | + f.pack(); | |
108 | + | |
109 | + } catch (Exception e) { | |
110 | + e.printStackTrace(); | |
111 | + getLogger().log(Level.SEVERE, "Error on startup", e); | |
112 | + JOptionPane.showMessageDialog(null, e.getMessage(), getProperty("id"), JOptionPane.ERROR_MESSAGE); | |
113 | + exit(); | |
114 | + } | |
115 | + } | |
116 | + | |
117 | + /* | |
118 | + * MAC OS X HOOKS | |
119 | + */ | |
120 | + private void registerForMacOSXEvents(PreferencesWindow pf, AboutWindow af) { | |
121 | + if (Util.MAC_OS_X) { | |
122 | + try { | |
123 | + OSXAdapter.setQuitHandler(this, WiimoteWhiteboard.class.getDeclaredMethod("quitApp", (Class[])null)); | |
124 | + if (!Util.INSIDE_APP_BUNDLE) { | |
125 | + OSXAdapter.setAboutHandler(af, AboutWindow.class.getDeclaredMethod("about", (Class[])null)); | |
126 | + } | |
127 | + OSXAdapter.setPreferencesHandler(pf, PreferencesWindow.class.getDeclaredMethod("preferences", (Class[])null)); | |
128 | + } catch (Exception e) { | |
129 | + e.printStackTrace(); | |
130 | + } | |
131 | + } | |
132 | + } | |
133 | + | |
134 | + @Action | |
135 | + public boolean quitApp() { | |
136 | + exit(); | |
137 | + return false; | |
138 | + } | |
139 | + | |
140 | + @Action | |
141 | + public void donate() { | |
142 | + BareBonesBrowserLaunch.openURL(getProperty("donateURL")); | |
143 | + } | |
144 | + | |
145 | + public static String getProperty(String key) { | |
146 | + return Util.getResourceMap(WiimoteWhiteboard.class).getString("Application."+key); | |
147 | + } | |
148 | + | |
149 | + public static Logger getLogger() { | |
150 | + return Logger.getLogger("wiimotewhiteboard"); | |
151 | + } | |
152 | + | |
153 | +} | ... | ... |
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/WiimoteWhiteboard.java~
0 → 100644
... | ... | @@ -0,0 +1,159 @@ |
1 | +package org.mote.wiimote.whiteboard; | |
2 | + | |
3 | +import java.util.Locale; | |
4 | +import java.util.logging.Level; | |
5 | +import java.util.logging.Logger; | |
6 | + | |
7 | +import javax.swing.JFrame; | |
8 | +import javax.swing.JOptionPane; | |
9 | + | |
10 | +import org.jdesktop.application.Action; | |
11 | +import org.jdesktop.application.Application; | |
12 | +import org.jdesktop.application.SingleFrameApplication; | |
13 | +import org.mote.wiimote.whiteboard.calibration.CalibrationPersistence; | |
14 | +import org.mote.wiimote.whiteboard.calibration.WiimoteCalibration; | |
15 | +import org.mote.wiimote.whiteboard.calibration.WiimoteCalibration.CalibrationEvent; | |
16 | +import org.mote.wiimote.whiteboard.gui.AboutWindow; | |
17 | +import org.mote.wiimote.whiteboard.gui.HelpHandler; | |
18 | +import org.mote.wiimote.whiteboard.gui.LogWindow; | |
19 | +import org.mote.wiimote.whiteboard.gui.MainPanel; | |
20 | +import org.mote.wiimote.whiteboard.gui.MenuBar; | |
21 | +import org.mote.wiimote.whiteboard.gui.PreferencesWindow; | |
22 | +import org.mote.wiimote.whiteboard.mouse.Mouse; | |
23 | +import org.mote.wiimote.whiteboard.preferences.WWPreferences; | |
24 | +import org.mote.wiimote.whiteboard.tuio.TuioTransmitter; | |
25 | +import org.mote.wiimote.whiteboard.util.BareBonesBrowserLaunch; | |
26 | +import org.mote.wiimote.whiteboard.util.UpdateNotifier; | |
27 | +import org.mote.wiimote.whiteboard.util.Util; | |
28 | +import org.mote.wiimote.whiteboard.util.WiiRemoteJErrorHandler; | |
29 | + | |
30 | +import wiiremotej.WiiRemoteJ; | |
31 | +import apple.dts.samplecode.osxadapter.OSXAdapter; | |
32 | + | |
33 | +public class WiimoteWhiteboard extends SingleFrameApplication { | |
34 | + | |
35 | + public static void main(String args[]) { | |
36 | + if (Util.MAC_OS_X && !Util.INSIDE_APP_BUNDLE) { | |
37 | + System.setProperty("apple.laf.useScreenMenuBar", "true"); | |
38 | + System.setProperty("com.apple.mrj.application.apple.menu.about.name", getProperty("id")); | |
39 | + } | |
40 | + System.setProperty("bluecove.jsr82.psm_minimum_off", "true"); | |
41 | + | |
42 | + String lang = WWPreferences.getPreferences().getLanguage(); | |
43 | + if (lang.length() > 0) { | |
44 | + String[] langParts = lang.split("_"); | |
45 | + switch (langParts.length) { | |
46 | + case 1: | |
47 | + Locale.setDefault(new Locale(langParts[0])); | |
48 | + break; | |
49 | + case 2: | |
50 | + Locale.setDefault(new Locale(langParts[0], langParts[1])); | |
51 | + break; | |
52 | + } | |
53 | + } | |
54 | + Application.launch(WiimoteWhiteboard.class, args); | |
55 | + } | |
56 | + | |
57 | + @Override | |
58 | + protected void startup() { | |
59 | + try { | |
60 | + new Thread(new Runnable() { | |
61 | + public void run() { | |
62 | + // blocking | |
63 | + if (WWPreferences.getPreferences().checkForUpdates()) | |
64 | + UpdateNotifier.checkForUpdate(getProperty("version")); | |
65 | + } | |
66 | + }).start(); | |
67 | + | |
68 | + Logger.getLogger("wiimotewhiteboard").setUseParentHandlers(false); | |
69 | + | |
70 | + final JFrame f = getMainFrame(); | |
71 | + LogWindow lw = new LogWindow(); | |
72 | + | |
73 | + final WiimoteCalibration calibration = new WiimoteCalibration(); | |
74 | + WiimoteDataHandler dh = new WiimoteDataHandler(calibration); | |
75 | + | |
76 | +// new IRDotLogger(dh); | |
77 | + | |
78 | + MainPanel mp = new MainPanel(dh, calibration); | |
79 | + AboutWindow af = new AboutWindow(); | |
80 | + HelpHandler hh = new HelpHandler(); | |
81 | + PreferencesWindow pf = new PreferencesWindow(mp, hh); | |
82 | + f.setJMenuBar(new MenuBar(pf, af, hh, lw)); | |
83 | + registerForMacOSXEvents(pf, af); | |
84 | + | |
85 | + // update Mouse's screen | |
86 | + calibration.addCalibrationEventListener(new WiimoteCalibration.CalibrationEventListener() { | |
87 | + public void calibrationEvent(CalibrationEvent e) { | |
88 | + if (e == CalibrationEvent.SCREEN_CHANGED) | |
89 | + Mouse.setScreen(calibration.getScreen()); | |
90 | + } | |
91 | + }); | |
92 | + | |
93 | + // add persistence as last listener because it indirectly generates new calibration events | |
94 | + new CalibrationPersistence(calibration); | |
95 | + // TODO save last used screen? | |
96 | + calibration.setScreen(WiimoteCalibration.DEFAULT_SCREEN); | |
97 | + | |
98 | + new TuioTransmitter(dh, calibration); | |
99 | + | |
100 | + WiiRemoteJ.setConsoleLoggingErrors(); | |
101 | + Logger.getLogger("wiiremotej").setLevel(Level.ALL); | |
102 | + Logger.getLogger("wiiremotej").addHandler(new WiiRemoteJErrorHandler(dh)); | |
103 | + | |
104 | + try { | |
105 | + // restore session manually before 'show' on the main panel is | |
106 | + // called. this is a fix to the problem that the session is not | |
107 | + // restored; apparently because mainFrame is not resizable | |
108 | + getContext().getSessionStorage().restore(f, "mainFrame.session.xml"); | |
109 | + } catch (Exception e) {} | |
110 | + | |
111 | + show(mp); | |
112 | + // f.pack() is called in 'show' above if f.isValid() == false | |
113 | + f.pack(); | |
114 | + | |
115 | + } catch (Exception e) { | |
116 | + e.printStackTrace(); | |
117 | + getLogger().log(Level.SEVERE, "Error on startup", e); | |
118 | + JOptionPane.showMessageDialog(null, e.getMessage(), getProperty("id"), JOptionPane.ERROR_MESSAGE); | |
119 | + exit(); | |
120 | + } | |
121 | + } | |
122 | + | |
123 | + /* | |
124 | + * MAC OS X HOOKS | |
125 | + */ | |
126 | + private void registerForMacOSXEvents(PreferencesWindow pf, AboutWindow af) { | |
127 | + if (Util.MAC_OS_X) { | |
128 | + try { | |
129 | + OSXAdapter.setQuitHandler(this, WiimoteWhiteboard.class.getDeclaredMethod("quitApp", (Class[])null)); | |
130 | + if (!Util.INSIDE_APP_BUNDLE) { | |
131 | + OSXAdapter.setAboutHandler(af, AboutWindow.class.getDeclaredMethod("about", (Class[])null)); | |
132 | + } | |
133 | + OSXAdapter.setPreferencesHandler(pf, PreferencesWindow.class.getDeclaredMethod("preferences", (Class[])null)); | |
134 | + } catch (Exception e) { | |
135 | + e.printStackTrace(); | |
136 | + } | |
137 | + } | |
138 | + } | |
139 | + | |
140 | + @Action | |
141 | + public boolean quitApp() { | |
142 | + exit(); | |
143 | + return false; | |
144 | + } | |
145 | + | |
146 | + @Action | |
147 | + public void donate() { | |
148 | + BareBonesBrowserLaunch.openURL(getProperty("donateURL")); | |
149 | + } | |
150 | + | |
151 | + public static String getProperty(String key) { | |
152 | + return Util.getResourceMap(WiimoteWhiteboard.class).getString("Application."+key); | |
153 | + } | |
154 | + | |
155 | + public static Logger getLogger() { | |
156 | + return Logger.getLogger("wiimotewhiteboard"); | |
157 | + } | |
158 | + | |
159 | +} | ... | ... |
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/calibration/CalibrationPersistence.java
0 → 100644
... | ... | @@ -0,0 +1,53 @@ |
1 | +package org.mote.wiimote.whiteboard.calibration; | |
2 | + | |
3 | +import java.awt.DisplayMode; | |
4 | +import java.io.IOException; | |
5 | + | |
6 | +import org.jdesktop.application.Application; | |
7 | +import org.mote.wiimote.whiteboard.calibration.WiimoteCalibration.CalibrationEvent; | |
8 | +import org.mote.wiimote.whiteboard.calibration.WiimoteCalibration.CalibrationEventListener; | |
9 | + | |
10 | +public class CalibrationPersistence implements CalibrationEventListener { | |
11 | + | |
12 | + WiimoteCalibration calibration; | |
13 | + | |
14 | + public CalibrationPersistence(WiimoteCalibration calibration) { | |
15 | + this.calibration = calibration; | |
16 | + calibration.addCalibrationEventListener(this); | |
17 | + } | |
18 | + | |
19 | + public void calibrationEvent(CalibrationEvent e) { | |
20 | + switch (e) { | |
21 | + case SCREEN_CHANGED: | |
22 | + loadCalibrationData(); | |
23 | + break; | |
24 | + case FINISHED: | |
25 | + saveCalibrationData(); | |
26 | + break; | |
27 | + } | |
28 | + } | |
29 | + | |
30 | + private String calibrationFileName() { | |
31 | + DisplayMode dm = calibration.getScreen().getDisplayMode(); | |
32 | + return String.format("calibration_%d_%dx%d.txt", calibration.getScreenNumber(), dm.getWidth(), dm.getHeight()); | |
33 | + } | |
34 | + | |
35 | + private void loadCalibrationData() { | |
36 | + try { | |
37 | + String fileName = calibrationFileName(); | |
38 | + calibration.load(Application.getInstance().getContext().getLocalStorage().openInputFile(fileName)); | |
39 | + } catch (IOException e) { | |
40 | + // ignore | |
41 | + } | |
42 | + } | |
43 | + | |
44 | + private void saveCalibrationData() { | |
45 | + try { | |
46 | + String fileName = calibrationFileName(); | |
47 | + calibration.save(Application.getInstance().getContext().getLocalStorage().openOutputFile(fileName)); | |
48 | + } catch (IOException e) { | |
49 | + // ignore | |
50 | + } | |
51 | + } | |
52 | + | |
53 | +} | ... | ... |
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/calibration/CalibrationPersistence.java~
0 → 100644
... | ... | @@ -0,0 +1,82 @@ |
1 | +/* | |
2 | + * Copyright (C) 2008-2009, Uwe Schmidt | |
3 | + * | |
4 | + * Permission is hereby granted, free of charge, to any person obtaining a | |
5 | + * copy of this software and associated documentation files (the "Software"), | |
6 | + * to deal in the Software without restriction, including without limitation | |
7 | + * the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
8 | + * and/or sell copies of the Software, and to permit persons to whom the | |
9 | + * Software is furnished to do so, subject to the following conditions: | |
10 | + * | |
11 | + * The above copyright notice and this permission notice shall be included in | |
12 | + * all copies or substantial portions of the Software. | |
13 | + * | |
14 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
15 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
16 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
17 | + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
18 | + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |
19 | + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | |
20 | + * DEALINGS IN THE SOFTWARE. | |
21 | + * | |
22 | + * The Software uses a third-party library (WiiRemoteJ) which is not part of | |
23 | + * the Software and is subject to its own license. | |
24 | + */ | |
25 | + | |
26 | +package org.uweschmidt.wiimote.whiteboard.calibration; | |
27 | + | |
28 | +import java.awt.DisplayMode; | |
29 | +import java.io.IOException; | |
30 | + | |
31 | +import org.jdesktop.application.Application; | |
32 | +import org.uweschmidt.wiimote.whiteboard.calibration.WiimoteCalibration.CalibrationEvent; | |
33 | +import org.uweschmidt.wiimote.whiteboard.calibration.WiimoteCalibration.CalibrationEventListener; | |
34 | + | |
35 | +public class CalibrationPersistence implements CalibrationEventListener { | |
36 | + | |
37 | + WiimoteCalibration calibration; | |
38 | + | |
39 | + public CalibrationPersistence(WiimoteCalibration calibration) { | |
40 | + this.calibration = calibration; | |
41 | + calibration.addCalibrationEventListener(this); | |
42 | + } | |
43 | + | |
44 | + public void calibrationEvent(CalibrationEvent e) { | |
45 | + switch (e) { | |
46 | + case SCREEN_CHANGED: | |
47 | + loadCalibrationData(); | |
48 | + break; | |
49 | + case FINISHED: | |
50 | + saveCalibrationData(); | |
51 | + break; | |
52 | + } | |
53 | + } | |
54 | + | |
55 | +// private String safeFilename(String rawName) { | |
56 | +// return rawName.replaceAll("[\\W]", "").toLowerCase(); | |
57 | +// } | |
58 | + | |
59 | + private String calibrationFileName() { | |
60 | + DisplayMode dm = calibration.getScreen().getDisplayMode(); | |
61 | + return String.format("calibration_%d_%dx%d.txt", calibration.getScreenNumber(), dm.getWidth(), dm.getHeight()); | |
62 | + } | |
63 | + | |
64 | + private void loadCalibrationData() { | |
65 | + try { | |
66 | + String fileName = calibrationFileName(); | |
67 | + calibration.load(Application.getInstance().getContext().getLocalStorage().openInputFile(fileName)); | |
68 | + } catch (IOException e) { | |
69 | + // ignore | |
70 | + } | |
71 | + } | |
72 | + | |
73 | + private void saveCalibrationData() { | |
74 | + try { | |
75 | + String fileName = calibrationFileName(); | |
76 | + calibration.save(Application.getInstance().getContext().getLocalStorage().openOutputFile(fileName)); | |
77 | + } catch (IOException e) { | |
78 | + // ignore | |
79 | + } | |
80 | + } | |
81 | + | |
82 | +} | ... | ... |
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/calibration/PointClusterer.java
0 → 100644
... | ... | @@ -0,0 +1,72 @@ |
1 | +package org.mote.wiimote.whiteboard.calibration; | |
2 | + | |
3 | +import java.util.HashSet; | |
4 | +import java.util.Map; | |
5 | +import java.util.Set; | |
6 | + | |
7 | +import org.mote.wiimote.whiteboard.ds.IRDot; | |
8 | +import org.mote.wiimote.whiteboard.ds.Wiimote; | |
9 | +import org.mote.wiimote.whiteboard.preferences.WWPreferences; | |
10 | + | |
11 | +public class PointClusterer { | |
12 | + | |
13 | + static { | |
14 | + if (WWPreferences.WIIMOTES > 2) | |
15 | + throw new RuntimeException("Clustering for more than 2 Wiimotes not implemented yet."); | |
16 | + } | |
17 | + | |
18 | + public static IRDot[][] cluster(Map<Wiimote, IRDot[]> data) { | |
19 | + IRDot[][] cluster = new IRDot[4][]; | |
20 | + | |
21 | + if (data.size() == 1) { | |
22 | + IRDot[] values = data.values().iterator().next(); | |
23 | + int j = 0; | |
24 | + for (int i = 0; i < 4; i++) | |
25 | + if (values[i] != null) | |
26 | + cluster[j++] = new IRDot[] { values[i] }; | |
27 | + return cluster; | |
28 | + } | |
29 | + | |
30 | + Wiimote[] wiimotes = new Wiimote[data.keySet().size()]; | |
31 | + data.keySet().toArray(wiimotes); | |
32 | + | |
33 | + Set<IRDot> used = new HashSet<IRDot>(); | |
34 | + int c = 0; | |
35 | + | |
36 | + for (int a = 0; a < 2; a++) { | |
37 | + | |
38 | + for (int i = 0; i < 4; i++) { | |
39 | + IRDot dot1 = data.get(wiimotes[a])[i]; | |
40 | + if (dot1 == null || used.contains(dot1)) | |
41 | + continue; | |
42 | + | |
43 | + IRDot minDot = null; | |
44 | + double minDist = Double.POSITIVE_INFINITY; | |
45 | + | |
46 | + for (int j = 0; j < 4; j++) { | |
47 | + IRDot dot2 = data.get(wiimotes[(a+1)%2])[j]; | |
48 | + if (dot2 == null || used.contains(dot2)) | |
49 | + continue; | |
50 | + | |
51 | + double dist = dot1.distance(dot2); | |
52 | + if (dist < minDist) { | |
53 | + minDot = dot2; | |
54 | + minDist = dist; | |
55 | + } | |
56 | + } | |
57 | + | |
58 | + used.add(dot1); | |
59 | + if (minDot == null) { | |
60 | + cluster[c++] = new IRDot[] { dot1 }; | |
61 | + } else { | |
62 | + cluster[c++] = new IRDot[] { dot1, minDot }; | |
63 | + used.add(minDot); | |
64 | + } | |
65 | + | |
66 | + } | |
67 | + } | |
68 | + | |
69 | + return cluster; | |
70 | + } | |
71 | + | |
72 | +} | ... | ... |
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/calibration/PointClusterer.java~
0 → 100644
... | ... | @@ -0,0 +1,101 @@ |
1 | +/* | |
2 | + * Copyright (C) 2008-2009, Uwe Schmidt | |
3 | + * | |
4 | + * Permission is hereby granted, free of charge, to any person obtaining a | |
5 | + * copy of this software and associated documentation files (the "Software"), | |
6 | + * to deal in the Software without restriction, including without limitation | |
7 | + * the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
8 | + * and/or sell copies of the Software, and to permit persons to whom the | |
9 | + * Software is furnished to do so, subject to the following conditions: | |
10 | + * | |
11 | + * The above copyright notice and this permission notice shall be included in | |
12 | + * all copies or substantial portions of the Software. | |
13 | + * | |
14 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
15 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
16 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
17 | + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
18 | + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |
19 | + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | |
20 | + * DEALINGS IN THE SOFTWARE. | |
21 | + * | |
22 | + * The Software uses a third-party library (WiiRemoteJ) which is not part of | |
23 | + * the Software and is subject to its own license. | |
24 | + */ | |
25 | + | |
26 | +package org.uweschmidt.wiimote.whiteboard.calibration; | |
27 | + | |
28 | +import java.util.HashSet; | |
29 | +import java.util.Map; | |
30 | +import java.util.Set; | |
31 | + | |
32 | +import org.uweschmidt.wiimote.whiteboard.ds.IRDot; | |
33 | +import org.uweschmidt.wiimote.whiteboard.ds.Wiimote; | |
34 | +import org.uweschmidt.wiimote.whiteboard.preferences.WWPreferences; | |
35 | + | |
36 | +public class PointClusterer { | |
37 | + | |
38 | + static { | |
39 | + if (WWPreferences.WIIMOTES > 2) | |
40 | + throw new RuntimeException("Clustering for more than 2 Wiimotes not implemented yet."); | |
41 | + } | |
42 | + | |
43 | + /** | |
44 | + * @return array of clusters (first index = cluster number, second index = dots in cluster) | |
45 | + */ | |
46 | + public static IRDot[][] cluster(Map<Wiimote, IRDot[]> data) { | |
47 | + IRDot[][] cluster = new IRDot[4][]; | |
48 | + | |
49 | + // trivial case | |
50 | + if (data.size() == 1) { | |
51 | + IRDot[] values = data.values().iterator().next(); | |
52 | + int j = 0; | |
53 | + for (int i = 0; i < 4; i++) | |
54 | + if (values[i] != null) | |
55 | + cluster[j++] = new IRDot[] { values[i] }; | |
56 | + return cluster; | |
57 | + } | |
58 | + | |
59 | + Wiimote[] wiimotes = new Wiimote[data.keySet().size()]; | |
60 | + data.keySet().toArray(wiimotes); | |
61 | + | |
62 | + Set<IRDot> used = new HashSet<IRDot>(); | |
63 | + int c = 0; | |
64 | + | |
65 | + for (int a = 0; a < 2; a++) { | |
66 | + | |
67 | + for (int i = 0; i < 4; i++) { | |
68 | + IRDot dot1 = data.get(wiimotes[a])[i]; | |
69 | + if (dot1 == null || used.contains(dot1)) | |
70 | + continue; | |
71 | + | |
72 | + IRDot minDot = null; | |
73 | + double minDist = Double.POSITIVE_INFINITY; | |
74 | + | |
75 | + for (int j = 0; j < 4; j++) { | |
76 | + IRDot dot2 = data.get(wiimotes[(a+1)%2])[j]; | |
77 | + if (dot2 == null || used.contains(dot2)) | |
78 | + continue; | |
79 | + | |
80 | + double dist = dot1.distance(dot2); | |
81 | + if (dist < minDist) { | |
82 | + minDot = dot2; | |
83 | + minDist = dist; | |
84 | + } | |
85 | + } | |
86 | + | |
87 | + used.add(dot1); | |
88 | + if (minDot == null) { | |
89 | + cluster[c++] = new IRDot[] { dot1 }; | |
90 | + } else { | |
91 | + cluster[c++] = new IRDot[] { dot1, minDot }; | |
92 | + used.add(minDot); | |
93 | + } | |
94 | + | |
95 | + } | |
96 | + } | |
97 | + | |
98 | + return cluster; | |
99 | + } | |
100 | + | |
101 | +} | ... | ... |
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/calibration/WiimoteCalibration.java
0 → 100644
... | ... | @@ -0,0 +1,511 @@ |
1 | +package org.mote.wiimote.whiteboard.calibration; | |
2 | +import java.awt.Color; | |
3 | +import java.awt.Graphics; | |
4 | +import java.awt.Graphics2D; | |
5 | +import java.awt.GraphicsDevice; | |
6 | +import java.awt.GraphicsEnvironment; | |
7 | +import java.awt.Image; | |
8 | +import java.awt.Point; | |
9 | +import java.awt.Rectangle; | |
10 | +import java.awt.RenderingHints; | |
11 | +import java.awt.event.KeyAdapter; | |
12 | +import java.awt.event.KeyEvent; | |
13 | +import java.awt.geom.Point2D; | |
14 | +import java.io.BufferedReader; | |
15 | +import java.io.IOException; | |
16 | +import java.io.InputStream; | |
17 | +import java.io.InputStreamReader; | |
18 | +import java.io.OutputStream; | |
19 | +import java.io.PrintStream; | |
20 | +import java.util.Arrays; | |
21 | +import java.util.Collection; | |
22 | +import java.util.HashMap; | |
23 | +import java.util.HashSet; | |
24 | +import java.util.LinkedHashMap; | |
25 | +import java.util.LinkedHashSet; | |
26 | +import java.util.Locale; | |
27 | +import java.util.Map; | |
28 | +import java.util.Set; | |
29 | + | |
30 | +import javax.media.jai.PerspectiveTransform; | |
31 | +import javax.swing.Icon; | |
32 | +import javax.swing.ImageIcon; | |
33 | +import javax.swing.JFrame; | |
34 | +import javax.swing.JLabel; | |
35 | +import javax.swing.JOptionPane; | |
36 | +import javax.swing.JPanel; | |
37 | +import javax.swing.SwingConstants; | |
38 | + | |
39 | +import org.mote.wiimote.whiteboard.WiimoteWhiteboard; | |
40 | +import org.mote.wiimote.whiteboard.ds.IRDot; | |
41 | +import org.mote.wiimote.whiteboard.ds.Wiimote; | |
42 | +import org.mote.wiimote.whiteboard.util.Util; | |
43 | + | |
44 | +public class WiimoteCalibration { | |
45 | + | |
46 | + // list of all "rectangles" of the 3x3 calibration grid, larger to smaller | |
47 | + private static final CalibrationState[][] VALID_STATES = { | |
48 | + {CalibrationState.UPPER_LEFT, CalibrationState.UPPER_RIGHT, CalibrationState.LOWER_RIGHT, CalibrationState.LOWER_LEFT}, | |
49 | + {CalibrationState.UPPER_LEFT, CalibrationState.TOP_MIDDLE, CalibrationState.BOTTOM_MIDDLE, CalibrationState.LOWER_LEFT}, | |
50 | + {CalibrationState.TOP_MIDDLE, CalibrationState.UPPER_RIGHT, CalibrationState.LOWER_RIGHT, CalibrationState.BOTTOM_MIDDLE}, | |
51 | + {CalibrationState.UPPER_LEFT, CalibrationState.UPPER_RIGHT, CalibrationState.EAST_MIDDLE, CalibrationState.WEST_MIDDLE}, | |
52 | + {CalibrationState.WEST_MIDDLE, CalibrationState.EAST_MIDDLE, CalibrationState.LOWER_RIGHT, CalibrationState.LOWER_LEFT}, | |
53 | + {CalibrationState.WEST_MIDDLE, CalibrationState.CENTER, CalibrationState.BOTTOM_MIDDLE, CalibrationState.LOWER_LEFT}, | |
54 | + {CalibrationState.UPPER_LEFT, CalibrationState.TOP_MIDDLE, CalibrationState.CENTER, CalibrationState.WEST_MIDDLE}, | |
55 | + {CalibrationState.TOP_MIDDLE, CalibrationState.UPPER_RIGHT, CalibrationState.EAST_MIDDLE, CalibrationState.CENTER}, | |
56 | + {CalibrationState.CENTER, CalibrationState.EAST_MIDDLE, CalibrationState.LOWER_RIGHT, CalibrationState.BOTTOM_MIDDLE}, | |
57 | + }; | |
58 | + | |
59 | + public static final GraphicsDevice DEFAULT_SCREEN = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice(); | |
60 | + | |
61 | + public static enum CalibrationEvent { | |
62 | + SCREEN_CHANGED, STARTED, FINISHED, ABORTED, LOADED, SAVED; | |
63 | + }; | |
64 | + | |
65 | + public static interface CalibrationEventListener { | |
66 | + public void calibrationEvent(CalibrationEvent e); | |
67 | + } | |
68 | + | |
69 | + public static enum CalibrationState { | |
70 | + DONE(0, 0, null), CENTER(.5, .5, DONE), EAST_MIDDLE(.9, .5, CENTER), WEST_MIDDLE(.1, .5, EAST_MIDDLE), TOP_MIDDLE(.5, .1, WEST_MIDDLE), BOTTOM_MIDDLE(.5, .9, TOP_MIDDLE), LOWER_LEFT(.1, .9, BOTTOM_MIDDLE), LOWER_RIGHT(.9, .9, LOWER_LEFT), UPPER_RIGHT(.9, .1, LOWER_RIGHT), UPPER_LEFT(.1, .1, UPPER_RIGHT), PENDING(0, 0, UPPER_LEFT); | |
71 | + | |
72 | + public static final CalibrationState REGULAR_END = LOWER_LEFT; | |
73 | + private final double xMargin, yMargin; | |
74 | + private final CalibrationState next; | |
75 | + | |
76 | + private CalibrationState(double xMargin, double yMargin, CalibrationState next) { | |
77 | + this.xMargin = xMargin; | |
78 | + this.yMargin = yMargin; | |
79 | + this.next = next; | |
80 | + } | |
81 | + | |
82 | + public int getX(Rectangle bounds) { | |
83 | + return bounds.x + (int) Math.round(bounds.width * xMargin); | |
84 | + } | |
85 | + | |
86 | + public int getY(Rectangle bounds) { | |
87 | + return bounds.y + (int) Math.round(bounds.height * yMargin); | |
88 | + } | |
89 | + | |
90 | + public CalibrationState getNext() { | |
91 | + return next; | |
92 | + } | |
93 | + }; | |
94 | + | |
95 | + private static final double EPS = .05; | |
96 | + | |
97 | + private final CalibrationFrame calibrationFrame; | |
98 | + private final Set<CalibrationEventListener> listener = new LinkedHashSet<CalibrationEventListener>(); | |
99 | + | |
100 | + | |
101 | + private GraphicsDevice screen = null; | |
102 | + private int screenNumber = -1; | |
103 | + private Rectangle bounds; | |
104 | + private int sc = 0; | |
105 | + private boolean stepChange = false; | |
106 | + private boolean checkPoints = false; | |
107 | + private Collection<Wiimote> wiimotes; | |
108 | + private CalibrationState state = CalibrationState.PENDING; | |
109 | + | |
110 | + // last calibration point for a wiimote, always non-null | |
111 | + private Map<Wiimote, Point2D> last = new HashMap<Wiimote, Point2D>(); | |
112 | + // transformer for wiimote | |
113 | + private Map<String, PerspectiveTransform> transformer = new LinkedHashMap<String, PerspectiveTransform>(); | |
114 | + // all visible calibration points for each wiimote (can be more than 4) | |
115 | + private Map<Wiimote, Map<CalibrationState, Point2D>> points = new LinkedHashMap<Wiimote, Map<CalibrationState,Point2D>>(); | |
116 | + // final 16 value double array (4<->4 mapping) for each wiimote, used to create transformer | |
117 | + private Map<String, Double[]> finals = new LinkedHashMap<String, Double[]>(); | |
118 | + | |
119 | + | |
120 | + | |
121 | + public WiimoteCalibration() { | |
122 | + calibrationFrame = new CalibrationFrame(); | |
123 | + } | |
124 | + | |
125 | + public boolean setScreen(GraphicsDevice screen) { | |
126 | + if (!inProgress()) { | |
127 | + this.screen = screen; | |
128 | + bounds = screen.getDefaultConfiguration().getBounds(); | |
129 | + calibrationFrame.setBounds(bounds); | |
130 | + | |
131 | + // get "screen number" | |
132 | + GraphicsDevice[] gds = GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices(); | |
133 | + for (int i = 0; i < gds.length; i++) { | |
134 | + if (gds[i] == screen) { | |
135 | + screenNumber = i + 1; | |
136 | + break; | |
137 | + } | |
138 | + } | |
139 | + | |
140 | + state = CalibrationState.PENDING; | |
141 | + transformer.clear(); | |
142 | + notifyListener(CalibrationEvent.SCREEN_CHANGED); | |
143 | + return true; | |
144 | + } else { | |
145 | + System.err.println("Calibration in Progress."); | |
146 | + return false; | |
147 | + } | |
148 | + } | |
149 | + | |
150 | + public GraphicsDevice getScreen() { | |
151 | + return screen; | |
152 | + } | |
153 | + | |
154 | + public int getScreenNumber() { | |
155 | + return screenNumber; | |
156 | + } | |
157 | + | |
158 | + @SuppressWarnings("serial") | |
159 | + private class CalibrationFrame extends JFrame { | |
160 | + private final ImageIcon VISIBLE = new ImageIcon(WiimoteCalibration.class.getResource("resources/icons/francisco-ok.png")); | |
161 | + private final ImageIcon NOT_VISIBLE = new ImageIcon(WiimoteCalibration.class.getResource("resources/icons/francisco-warning.png")); | |
162 | + private final Image CROSS_HAIR = new ImageIcon(WiimoteCalibration.class.getResource("resources/icons/francisco-crosshair.png")).getImage(); | |
163 | + | |
164 | + public CalibrationFrame() { | |
165 | + super("Calibration"); | |
166 | + setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); | |
167 | + setBackground(Color.WHITE); | |
168 | + ((JPanel)getContentPane()).setOpaque(true); | |
169 | + setLayout(null); | |
170 | + setUndecorated(true); | |
171 | + setAlwaysOnTop(true); | |
172 | + addKeyListener(new KeyAdapter() { | |
173 | + @Override | |
174 | + public void keyPressed(KeyEvent e) { | |
175 | + if (e.getKeyCode() == KeyEvent.VK_ESCAPE) { | |
176 | + state = isAnyCalibrated(wiimotes) ? CalibrationState.DONE : CalibrationState.PENDING; | |
177 | + if (screen.getFullScreenWindow() == calibrationFrame) | |
178 | + screen.setFullScreenWindow(null); | |
179 | + setVisible(false); | |
180 | + notifyListener(CalibrationEvent.ABORTED); | |
181 | + } | |
182 | + } | |
183 | + }); | |
184 | + } | |
185 | + | |
186 | + private JLabel statusLabel(Icon icon, int id, int x, int y) { | |
187 | + JLabel l = new JLabel(icon, SwingConstants.LEFT); | |
188 | + if (wiimotes.size() > 1) l.setText(String.valueOf(id)); | |
189 | + int w = 100; | |
190 | + int h = icon.getIconHeight(); | |
191 | + x = x - icon.getIconWidth()/2; | |
192 | + y = y - wiimotes.size()*h/2; | |
193 | + l.setBounds(x, y + (id-1)*h, w, h); | |
194 | + return l; | |
195 | + } | |
196 | + | |
197 | + public void finished(CalibrationState s) { | |
198 | + int x = s.getX(bounds) - bounds.x; | |
199 | + int y = s.getY(bounds) - bounds.y; | |
200 | + | |
201 | + for (Wiimote wiimote : points.keySet()) { | |
202 | + add(statusLabel(points.get(wiimote).get(s) != null ? VISIBLE : NOT_VISIBLE, wiimote.getId(), x, y)); | |
203 | + } | |
204 | + | |
205 | + repaint(); | |
206 | + } | |
207 | + | |
208 | + public void reset() { | |
209 | + getContentPane().removeAll(); | |
210 | + JLabel info = Util.newComponent(JLabel.class, "infoLabel"); | |
211 | + info.setFont(info.getFont().deriveFont(20f)); | |
212 | + info.setHorizontalAlignment(SwingConstants.CENTER); | |
213 | + int w = bounds.width; | |
214 | + int h = 200; | |
215 | + info.setBounds(bounds.width/2 - w/2, bounds.height/3 - h/2, w, h); | |
216 | + add(info); | |
217 | + Util.getResourceMap(WiimoteCalibration.class).injectComponents(this); | |
218 | + } | |
219 | + | |
220 | + @Override | |
221 | + public void paint(Graphics g) { | |
222 | + super.paint(g); | |
223 | + ((Graphics2D) g).setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); | |
224 | + | |
225 | + if (state != CalibrationState.DONE && state != CalibrationState.PENDING) { | |
226 | + int x = state.getX(bounds) - bounds.x; | |
227 | + int y = state.getY(bounds) - bounds.y; | |
228 | + g.drawImage(CROSS_HAIR, x - CROSS_HAIR.getWidth(null)/2 + 1, y - CROSS_HAIR.getHeight(null)/2 + 1, null); | |
229 | + } | |
230 | + } | |
231 | + | |
232 | + } | |
233 | + | |
234 | + public void start(Collection<Wiimote> wiimotes) { | |
235 | + last.clear(); | |
236 | + points.clear(); | |
237 | + finals.clear(); | |
238 | + calibrationFrame.reset(); | |
239 | + stepChange = false; | |
240 | + sc = 0; | |
241 | + checkPoints = false; | |
242 | + state = CalibrationState.PENDING.getNext(); | |
243 | + | |
244 | + this.wiimotes = wiimotes; | |
245 | + for (Wiimote wiimote : wiimotes) { | |
246 | + points.put(wiimote, new LinkedHashMap<CalibrationState, Point2D>()); | |
247 | + last.put(wiimote, new Point2D.Double(-1,-1)); | |
248 | + } | |
249 | + | |
250 | + if (screen == null) | |
251 | + setScreen(DEFAULT_SCREEN); | |
252 | + | |
253 | + if (Util.MAC_OS_X && screen == DEFAULT_SCREEN) | |
254 | + screen.setFullScreenWindow(calibrationFrame); | |
255 | + | |
256 | + calibrationFrame.repaint(); | |
257 | + calibrationFrame.setVisible(true); | |
258 | + notifyListener(CalibrationEvent.STARTED); | |
259 | + } | |
260 | + | |
261 | + public boolean inProgress() { | |
262 | + return !isPending() && !isDone(); | |
263 | + } | |
264 | + | |
265 | + public boolean isPending() { | |
266 | + return state == CalibrationState.PENDING; | |
267 | + } | |
268 | + | |
269 | + public boolean isDone() { | |
270 | + return state == CalibrationState.DONE; | |
271 | + } | |
272 | + | |
273 | + public Map<String, Double[]> getFinals() { | |
274 | + return finals; | |
275 | + } | |
276 | + | |
277 | + public boolean isCalibrated(Wiimote wiimote) { | |
278 | + return transformer.containsKey(wiimote.getAddress()); | |
279 | + } | |
280 | + | |
281 | + public boolean isAnyCalibrated(Collection<Wiimote> wiimotes) { | |
282 | + for (Wiimote wiimote : wiimotes) { | |
283 | + if (isCalibrated(wiimote)) | |
284 | + return true; | |
285 | + } | |
286 | + return false; | |
287 | + } | |
288 | + | |
289 | + public boolean process(Map<Wiimote, IRDot[]> data) { | |
290 | + | |
291 | + if (inProgress()) { | |
292 | + | |
293 | + for (Wiimote wiimote : points.keySet()) { | |
294 | + Point2D lastP = last.get(wiimote); | |
295 | + Point2D currentP = data.get(wiimote)[0]; | |
296 | + | |
297 | + if (currentP != null && lastP.distance(currentP) > EPS) { | |
298 | + | |
299 | + points.get(wiimote).put(state, currentP); | |
300 | + last.put(wiimote, currentP); | |
301 | + stepChange = true; | |
302 | + } | |
303 | + } | |
304 | + | |
305 | + // process at least 5 points to not miss a signal | |
306 | + if (stepChange && ++sc > 5) { | |
307 | + sc = 0; | |
308 | + stepChange = false; | |
309 | + CalibrationState current = state; | |
310 | + state = state.getNext(); | |
311 | + calibrationFrame.finished(current); | |
312 | + | |
313 | + if (checkPoints || current == CalibrationState.REGULAR_END) { | |
314 | + checkPoints = true; | |
315 | + if (calculateQuadsForWiimotes()) { | |
316 | + // OK, calibration finished | |
317 | + state = CalibrationState.DONE; | |
318 | + calculateTransformation(); | |
319 | + if (screen.getFullScreenWindow() == calibrationFrame) | |
320 | + screen.setFullScreenWindow(null); | |
321 | + calibrationFrame.setVisible(false); | |
322 | + notifyListener(CalibrationEvent.FINISHED); | |
323 | + return true; | |
324 | + } else { | |
325 | + // Go on | |
326 | + } | |
327 | + } | |
328 | + } | |
329 | + | |
330 | + if (state == CalibrationState.DONE) { | |
331 | + // PROBLEM, no solution found | |
332 | + state = CalibrationState.PENDING; | |
333 | + if (screen.getFullScreenWindow() == calibrationFrame) | |
334 | + screen.setFullScreenWindow(null); | |
335 | + calibrationFrame.setVisible(false); | |
336 | + notifyListener(CalibrationEvent.ABORTED); | |
337 | + new Thread(new Runnable() { | |
338 | + public void run() { | |
339 | + JOptionPane.showMessageDialog(null, Util.getResourceMap(WiimoteCalibration.class).getString("coverageError"), Util.getResourceMap(WiimoteCalibration.class).getString("calibrationFailed"), JOptionPane.ERROR_MESSAGE); | |
340 | + } | |
341 | + }).start(); | |
342 | + } | |
343 | + | |
344 | + return true; | |
345 | + } | |
346 | + | |
347 | + return false; | |
348 | + } | |
349 | + | |
350 | + private static boolean checkExist(Map<CalibrationState, Point2D> p, CalibrationState... states) { | |
351 | + for (CalibrationState s : states) { | |
352 | + if (p.get(s) == null) | |
353 | + return false; | |
354 | + } | |
355 | + return true; | |
356 | + } | |
357 | + | |
358 | + private boolean calculateQuadsForWiimotes() { | |
359 | + boolean success = true; | |
360 | + Set<CalibrationState> check = new HashSet<CalibrationState>(); | |
361 | + | |
362 | + for (Wiimote wiimote : points.keySet()) { | |
363 | + boolean ok = false; | |
364 | + | |
365 | + for (int i = 0; i < VALID_STATES.length; i++) { | |
366 | + if (checkExist(points.get(wiimote), VALID_STATES[i])) { | |
367 | + Map<CalibrationState, Point2D> calibrated = points.get(wiimote); | |
368 | + CalibrationState[] states = VALID_STATES[i]; | |
369 | + check.addAll(Arrays.asList(states)); | |
370 | + finals.put(wiimote.getAddress(), new Double[] { | |
371 | + calibrated.get(states[0]).getX(), calibrated.get(states[0]).getY(), | |
372 | + calibrated.get(states[1]).getX(), calibrated.get(states[1]).getY(), | |
373 | + calibrated.get(states[2]).getX(), calibrated.get(states[2]).getY(), | |
374 | + calibrated.get(states[3]).getX(), calibrated.get(states[3]).getY(), | |
375 | + (double)states[0].getX(bounds), (double)states[0].getY(bounds), | |
376 | + (double)states[1].getX(bounds), (double)states[1].getY(bounds), | |
377 | + (double)states[2].getX(bounds), (double)states[2].getY(bounds), | |
378 | + (double)states[3].getX(bounds), (double)states[3].getY(bounds) | |
379 | + }); | |
380 | + ok = true; | |
381 | + break; | |
382 | + } | |
383 | + } | |
384 | + | |
385 | + success = success && ok; | |
386 | + } | |
387 | + // check also if the whole screen is covered | |
388 | + return success && check.containsAll(Arrays.asList(VALID_STATES[0])); | |
389 | + } | |
390 | + | |
391 | + private void calculateTransformation() { | |
392 | + transformer.clear(); | |
393 | + for (String address : finals.keySet()) { | |
394 | + Double[] d = finals.get(address); | |
395 | + transformer.put(address, PerspectiveTransform.getQuadToQuad(d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7], d[8], d[9], d[10], d[11], d[12], d[13], d[14], d[15])); | |
396 | + } | |
397 | + } | |
398 | + | |
399 | + public Map<String, PerspectiveTransform> getTransformer() { | |
400 | + return transformer; | |
401 | + } | |
402 | + | |
403 | + public IRDot warp(int i, Wiimote wiimote, Map<Wiimote, IRDot[]> data) { | |
404 | + final PerspectiveTransform transform = transformer.get(wiimote.getAddress()); | |
405 | + if (transform == null || data.get(wiimote)[i] == null) return null; | |
406 | + else { | |
407 | + return (IRDot) transform.transform(data.get(wiimote)[i], new IRDot(data.get(wiimote)[i])); | |
408 | + } | |
409 | + } | |
410 | + | |
411 | + private Map<Wiimote, IRDot[]> warpIndividually(Map<Wiimote, IRDot[]> data) { | |
412 | + Map<Wiimote, IRDot[]> warped = new LinkedHashMap<Wiimote, IRDot[]>(); | |
413 | + IRDot[] p; | |
414 | + for (Wiimote wiimote : data.keySet()) { | |
415 | + warped.put(wiimote, p = new IRDot[4]); | |
416 | + for (int i = 0; i < 4; i++) { | |
417 | + p[i] = warp(i, wiimote, data); | |
418 | + } | |
419 | + } | |
420 | + return warped; | |
421 | + } | |
422 | + | |
423 | + public Point[] warp(Map<Wiimote, IRDot[]> data) { | |
424 | + Point[] warped = new Point[4]; | |
425 | + if (isDone() && isAnyCalibrated(data.keySet())) { | |
426 | + IRDot[][] cluster = PointClusterer.cluster(warpIndividually(data)); | |
427 | + | |
428 | + for (int i = 0; i < cluster.length; i++) { | |
429 | + if (cluster[i] == null) break; | |
430 | + double x = 0, y = 0; | |
431 | + int c = 0; | |
432 | + for (IRDot p : cluster[i]) { | |
433 | + x += p.getX(); | |
434 | + y += p.getY(); | |
435 | + c++; | |
436 | + break; | |
437 | + } | |
438 | + warped[i] = new Point((int) Math.round(x / c), (int) Math.round(y / c)); | |
439 | + } | |
440 | + } else { | |
441 | + System.err.println("Not calibrated."); | |
442 | + } | |
443 | + return warped; | |
444 | + } | |
445 | + | |
446 | + | |
447 | + public boolean load(InputStream is) throws IOException { | |
448 | + try { | |
449 | + BufferedReader in = new BufferedReader(new InputStreamReader(is)); | |
450 | + String address = null; | |
451 | + | |
452 | + while ((address = in.readLine()) != null) { | |
453 | + Double[] d = new Double[16]; | |
454 | + finals.put(address, d); | |
455 | + for (int i = 0; i < 4; i++) { | |
456 | + String[] p = in.readLine().split(" "); | |
457 | + for (int j = 0; j < 2; j++) { | |
458 | + d[i*2+j+0] = Double.valueOf(p[j+0]); | |
459 | + d[i*2+j+8] = Double.valueOf(p[j+2]); | |
460 | + } | |
461 | + } | |
462 | + } | |
463 | + | |
464 | + calculateTransformation(); | |
465 | + state = CalibrationState.DONE; | |
466 | + notifyListener(CalibrationEvent.LOADED); | |
467 | + return true; | |
468 | + | |
469 | + } catch (Exception e) { | |
470 | + e.printStackTrace(); | |
471 | + return false; | |
472 | + } | |
473 | + } | |
474 | + | |
475 | + public void save(OutputStream os) throws IOException { | |
476 | + PrintStream out = new PrintStream(os); | |
477 | + | |
478 | + for (String address : finals.keySet()) { | |
479 | + out.print(address); | |
480 | + Double[] d = finals.get(address); | |
481 | + for (int i = 0; i < 4; i++) { | |
482 | + out.printf(Locale.ENGLISH, "\n%f %f %.0f %.0f", d[i*2+0+0], d[i*2+1+0], d[i*2+0+8], d[i*2+1+8]); | |
483 | + } | |
484 | + out.println(); | |
485 | + } | |
486 | + out.close(); | |
487 | + | |
488 | + notifyListener(CalibrationEvent.SAVED); | |
489 | + } | |
490 | + | |
491 | + | |
492 | + /* | |
493 | + * LISTENER | |
494 | + */ | |
495 | + | |
496 | + public void addCalibrationEventListener(CalibrationEventListener l) { | |
497 | + listener.add(l); | |
498 | + } | |
499 | + | |
500 | + public void removeCalibrationEventListener(CalibrationEventListener l) { | |
501 | + listener.remove(l); | |
502 | + } | |
503 | + | |
504 | + private void notifyListener(CalibrationEvent e) { | |
505 | + WiimoteWhiteboard.getLogger().info("Calibration Event: " + e); | |
506 | + for (CalibrationEventListener l : listener) { | |
507 | + l.calibrationEvent(e); | |
508 | + } | |
509 | + } | |
510 | + | |
511 | +} | ... | ... |
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/calibration/WiimoteCalibration.java~
0 → 100644
... | ... | @@ -0,0 +1,511 @@ |
1 | +package org.mote.wiimote.whiteboard.calibration; | |
2 | +import java.awt.Color; | |
3 | +import java.awt.Graphics; | |
4 | +import java.awt.Graphics2D; | |
5 | +import java.awt.GraphicsDevice; | |
6 | +import java.awt.GraphicsEnvironment; | |
7 | +import java.awt.Image; | |
8 | +import java.awt.Point; | |
9 | +import java.awt.Rectangle; | |
10 | +import java.awt.RenderingHints; | |
11 | +import java.awt.event.KeyAdapter; | |
12 | +import java.awt.event.KeyEvent; | |
13 | +import java.awt.geom.Point2D; | |
14 | +import java.io.BufferedReader; | |
15 | +import java.io.IOException; | |
16 | +import java.io.InputStream; | |
17 | +import java.io.InputStreamReader; | |
18 | +import java.io.OutputStream; | |
19 | +import java.io.PrintStream; | |
20 | +import java.util.Arrays; | |
21 | +import java.util.Collection; | |
22 | +import java.util.HashMap; | |
23 | +import java.util.HashSet; | |
24 | +import java.util.LinkedHashMap; | |
25 | +import java.util.LinkedHashSet; | |
26 | +import java.util.Locale; | |
27 | +import java.util.Map; | |
28 | +import java.util.Set; | |
29 | + | |
30 | +import javax.media.jai.PerspectiveTransform; | |
31 | +import javax.swing.Icon; | |
32 | +import javax.swing.ImageIcon; | |
33 | +import javax.swing.JFrame; | |
34 | +import javax.swing.JLabel; | |
35 | +import javax.swing.JOptionPane; | |
36 | +import javax.swing.JPanel; | |
37 | +import javax.swing.SwingConstants; | |
38 | + | |
39 | +import org.mote.wiimote.whiteboard.WiimoteWhiteboard; | |
40 | +import org.mote.wiimote.whiteboard.ds.IRDot; | |
41 | +import org.mote.wiimote.whiteboard.ds.Wiimote; | |
42 | +import org.mote.wiimote.whiteboard.util.Util; | |
43 | + | |
44 | +public class WiimoteCalibration { | |
45 | + | |
46 | + // list of all "rectangles" of the 3x3 calibration grid, larger to smaller | |
47 | + private static final CalibrationState[][] VALID_STATES = { | |
48 | + {CalibrationState.UPPER_LEFT, CalibrationState.UPPER_RIGHT, CalibrationState.LOWER_RIGHT, CalibrationState.LOWER_LEFT}, | |
49 | + {CalibrationState.UPPER_LEFT, CalibrationState.TOP_MIDDLE, CalibrationState.BOTTOM_MIDDLE, CalibrationState.LOWER_LEFT}, | |
50 | + {CalibrationState.TOP_MIDDLE, CalibrationState.UPPER_RIGHT, CalibrationState.LOWER_RIGHT, CalibrationState.BOTTOM_MIDDLE}, | |
51 | + {CalibrationState.UPPER_LEFT, CalibrationState.UPPER_RIGHT, CalibrationState.EAST_MIDDLE, CalibrationState.WEST_MIDDLE}, | |
52 | + {CalibrationState.WEST_MIDDLE, CalibrationState.EAST_MIDDLE, CalibrationState.LOWER_RIGHT, CalibrationState.LOWER_LEFT}, | |
53 | + {CalibrationState.WEST_MIDDLE, CalibrationState.CENTER, CalibrationState.BOTTOM_MIDDLE, CalibrationState.LOWER_LEFT}, | |
54 | + {CalibrationState.UPPER_LEFT, CalibrationState.TOP_MIDDLE, CalibrationState.CENTER, CalibrationState.WEST_MIDDLE}, | |
55 | + {CalibrationState.TOP_MIDDLE, CalibrationState.UPPER_RIGHT, CalibrationState.EAST_MIDDLE, CalibrationState.CENTER}, | |
56 | + {CalibrationState.CENTER, CalibrationState.EAST_MIDDLE, CalibrationState.LOWER_RIGHT, CalibrationState.BOTTOM_MIDDLE}, | |
57 | + }; | |
58 | + | |
59 | + public static final GraphicsDevice DEFAULT_SCREEN = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice(); | |
60 | + | |
61 | + public static enum CalibrationEvent { | |
62 | + SCREEN_CHANGED, STARTED, FINISHED, ABORTED, LOADED, SAVED; | |
63 | + }; | |
64 | + | |
65 | + public static interface CalibrationEventListener { | |
66 | + public void calibrationEvent(CalibrationEvent e); | |
67 | + } | |
68 | + | |
69 | + public static enum CalibrationState { | |
70 | + DONE(0, 0, null), CENTER(.5, .5, DONE), EAST_MIDDLE(.9, .5, CENTER), WEST_MIDDLE(.1, .5, EAST_MIDDLE), TOP_MIDDLE(.5, .1, WEST_MIDDLE), BOTTOM_MIDDLE(.5, .9, TOP_MIDDLE), LOWER_LEFT(.1, .9, BOTTOM_MIDDLE), LOWER_RIGHT(.9, .9, LOWER_LEFT), UPPER_RIGHT(.9, .1, LOWER_RIGHT), UPPER_LEFT(.1, .1, UPPER_RIGHT), PENDING(0, 0, UPPER_LEFT); | |
71 | + | |
72 | + public static final CalibrationState REGULAR_END = LOWER_LEFT; | |
73 | + private final double xMargin, yMargin; | |
74 | + private final CalibrationState next; | |
75 | + | |
76 | + private CalibrationState(double xMargin, double yMargin, CalibrationState next) { | |
77 | + this.xMargin = xMargin; | |
78 | + this.yMargin = yMargin; | |
79 | + this.next = next; | |
80 | + } | |
81 | + | |
82 | + public int getX(Rectangle bounds) { | |
83 | + return bounds.x + (int) Math.round(bounds.width * xMargin); | |
84 | + } | |
85 | + | |
86 | + public int getY(Rectangle bounds) { | |
87 | + return bounds.y + (int) Math.round(bounds.height * yMargin); | |
88 | + } | |
89 | + | |
90 | + public CalibrationState getNext() { | |
91 | + return next; | |
92 | + } | |
93 | + }; | |
94 | + | |
95 | + private static final double EPS = .05; | |
96 | + | |
97 | + private final CalibrationFrame calibrationFrame; | |
98 | + private final Set<CalibrationEventListener> listener = new LinkedHashSet<CalibrationEventListener>(); | |
99 | + | |
100 | + | |
101 | + private GraphicsDevice screen = null; | |
102 | + private int screenNumber = -1; | |
103 | + private Rectangle bounds; | |
104 | + private int sc = 0; | |
105 | + private boolean stepChange = false; | |
106 | + private boolean checkPoints = false; | |
107 | + private Collection<Wiimote> wiimotes; | |
108 | + private CalibrationState state = CalibrationState.PENDING; | |
109 | + | |
110 | + // last calibration point for a wiimote, always non-null | |
111 | + private Map<Wiimote, Point2D> last = new HashMap<Wiimote, Point2D>(); | |
112 | + // transformer for wiimote | |
113 | + private Map<String, PerspectiveTransform> transformer = new LinkedHashMap<String, PerspectiveTransform>(); | |
114 | + // all visible calibration points for each wiimote (can be more than 4) | |
115 | + private Map<Wiimote, Map<CalibrationState, Point2D>> points = new LinkedHashMap<Wiimote, Map<CalibrationState,Point2D>>(); | |
116 | + // final 16 value double array (4<->4 mapping) for each wiimote, used to create transformer | |
117 | + private Map<String, Double[]> finals = new LinkedHashMap<String, Double[]>(); | |
118 | + | |
119 | + | |
120 | + | |
121 | + public WiimoteCalibration() { | |
122 | + calibrationFrame = new CalibrationFrame(); | |
123 | + } | |
124 | + | |
125 | + public boolean setScreen(GraphicsDevice screen) { | |
126 | + if (!inProgress()) { | |
127 | + this.screen = screen; | |
128 | + bounds = screen.getDefaultConfiguration().getBounds(); | |
129 | + calibrationFrame.setBounds(bounds); | |
130 | + | |
131 | + // get "screen number" | |
132 | + GraphicsDevice[] gds = GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices(); | |
133 | + for (int i = 0; i < gds.length; i++) { | |
134 | + if (gds[i] == screen) { | |
135 | + screenNumber = i + 1; | |
136 | + break; | |
137 | + } | |
138 | + } | |
139 | + | |
140 | + state = CalibrationState.PENDING; | |
141 | + transformer.clear(); | |
142 | + notifyListener(CalibrationEvent.SCREEN_CHANGED); | |
143 | + return true; | |
144 | + } else { | |
145 | + System.err.println("Calibration in Progress."); | |
146 | + return false; | |
147 | + } | |
148 | + } | |
149 | + | |
150 | + public GraphicsDevice getScreen() { | |
151 | + return screen; | |
152 | + } | |
153 | + | |
154 | + public int getScreenNumber() { | |
155 | + return screenNumber; | |
156 | + } | |
157 | + | |
158 | + @SuppressWarnings("serial") | |
159 | + private class CalibrationFrame extends JFrame { | |
160 | + private final ImageIcon VISIBLE = new ImageIcon(WiimoteCalibration.class.getResource("resources/icons/francisco-ok.png")); | |
161 | + private final ImageIcon NOT_VISIBLE = new ImageIcon(WiimoteCalibration.class.getResource("resources/icons/francisco-warning.png")); | |
162 | + private final Image CROSS_HAIR = new ImageIcon(WiimoteCalibration.class.getResource("resources/icons/francisco-crosshair.png")).getImage(); | |
163 | + | |
164 | + public CalibrationFrame() { | |
165 | + super("Calibration"); | |
166 | + setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); | |
167 | + setBackground(Color.WHITE); | |
168 | + ((JPanel)getContentPane()).setOpaque(true); | |
169 | + setLayout(null); | |
170 | + setUndecorated(true); | |
171 | + setAlwaysOnTop(true); | |
172 | + addKeyListener(new KeyAdapter() { | |
173 | + @Override | |
174 | + public void keyPressed(KeyEvent e) { | |
175 | + if (e.getKeyCode() == KeyEvent.VK_ESCAPE) { | |
176 | + state = isAnyCalibrated(wiimotes) ? CalibrationState.DONE : CalibrationState.PENDING; | |
177 | + if (screen.getFullScreenWindow() == calibrationFrame) | |
178 | + screen.setFullScreenWindow(null); | |
179 | + setVisible(false); | |
180 | + notifyListener(CalibrationEvent.ABORTED); | |
181 | + } | |
182 | + } | |
183 | + }); | |
184 | + } | |
185 | + | |
186 | + private JLabel statusLabel(Icon icon, int id, int x, int y) { | |
187 | + JLabel l = new JLabel(icon, SwingConstants.LEFT); | |
188 | + if (wiimotes.size() > 1) l.setText(String.valueOf(id)); | |
189 | + int w = 100; | |
190 | + int h = icon.getIconHeight(); | |
191 | + x = x - icon.getIconWidth()/2; | |
192 | + y = y - wiimotes.size()*h/2; | |
193 | + l.setBounds(x, y + (id-1)*h, w, h); | |
194 | + return l; | |
195 | + } | |
196 | + | |
197 | + public void finished(CalibrationState s) { | |
198 | + int x = s.getX(bounds) - bounds.x; | |
199 | + int y = s.getY(bounds) - bounds.y; | |
200 | + | |
201 | + for (Wiimote wiimote : points.keySet()) { | |
202 | + add(statusLabel(points.get(wiimote).get(s) != null ? VISIBLE : NOT_VISIBLE, wiimote.getId(), x, y)); | |
203 | + } | |
204 | + | |
205 | + repaint(); | |
206 | + } | |
207 | + | |
208 | + public void reset() { | |
209 | + getContentPane().removeAll(); | |
210 | + JLabel info = Util.newComponent(JLabel.class, "infoLabel"); | |
211 | + info.setFont(info.getFont().deriveFont(20f)); | |
212 | + info.setHorizontalAlignment(SwingConstants.CENTER); | |
213 | + int w = bounds.width; | |
214 | + int h = 200; | |
215 | + info.setBounds(bounds.width/2 - w/2, bounds.height/3 - h/2, w, h); | |
216 | + add(info); | |
217 | + Util.getResourceMap(WiimoteCalibration.class).injectComponents(this); | |
218 | + } | |
219 | + | |
220 | + @Override | |
221 | + public void paint(Graphics g) { | |
222 | + super.paint(g); | |
223 | + ((Graphics2D) g).setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); | |
224 | + | |
225 | + if (state != CalibrationState.DONE && state != CalibrationState.PENDING) { | |
226 | + int x = state.getX(bounds) - bounds.x; | |
227 | + int y = state.getY(bounds) - bounds.y; | |
228 | + g.drawImage(CROSS_HAIR, x - CROSS_HAIR.getWidth(null)/2 + 1, y - CROSS_HAIR.getHeight(null)/2 + 1, null); | |
229 | + } | |
230 | + } | |
231 | + | |
232 | + } | |
233 | + | |
234 | + public void start(Collection<Wiimote> wiimotes) { | |
235 | + last.clear(); | |
236 | + points.clear(); | |
237 | + finals.clear(); | |
238 | + calibrationFrame.reset(); | |
239 | + stepChange = false; | |
240 | + sc = 0; | |
241 | + checkPoints = false; | |
242 | + state = CalibrationState.PENDING.getNext(); | |
243 | + | |
244 | + this.wiimotes = wiimotes; | |
245 | + for (Wiimote wiimote : wiimotes) { | |
246 | + points.put(wiimote, new LinkedHashMap<CalibrationState, Point2D>()); | |
247 | + last.put(wiimote, new Point2D.Double(-1,-1)); | |
248 | + } | |
249 | + | |
250 | + if (screen == null) | |
251 | + setScreen(DEFAULT_SCREEN); | |
252 | + | |
253 | + if (Util.MAC_OS_X && screen == DEFAULT_SCREEN) | |
254 | + screen.setFullScreenWindow(calibrationFrame); | |
255 | + | |
256 | + calibrationFrame.repaint(); | |
257 | + calibrationFrame.setVisible(true); | |
258 | + notifyListener(CalibrationEvent.STARTED); | |
259 | + } | |
260 | + | |
261 | + public boolean inProgress() { | |
262 | + return !isPending() && !isDone(); | |
263 | + } | |
264 | + | |
265 | + public boolean isPending() { | |
266 | + return state == CalibrationState.PENDING; | |
267 | + } | |
268 | + | |
269 | + public boolean isDone() { | |
270 | + return state == CalibrationState.DONE; | |
271 | + } | |
272 | + | |
273 | + public Map<String, Double[]> getFinals() { | |
274 | + return finals; | |
275 | + } | |
276 | + | |
277 | + public boolean isCalibrated(Wiimote wiimote) { | |
278 | + return transformer.containsKey(wiimote.getAddress()); | |
279 | + } | |
280 | + | |
281 | + public boolean isAnyCalibrated(Collection<Wiimote> wiimotes) { | |
282 | + for (Wiimote wiimote : wiimotes) { | |
283 | + if (isCalibrated(wiimote)) | |
284 | + return true; | |
285 | + } | |
286 | + return false; | |
287 | + } | |
288 | + | |
289 | + public boolean process(Map<Wiimote, IRDot[]> data) { | |
290 | + | |
291 | + if (inProgress()) { | |
292 | + | |
293 | + for (Wiimote wiimote : points.keySet()) { | |
294 | + Point2D lastP = last.get(wiimote); | |
295 | + Point2D currentP = data.get(wiimote)[0]; | |
296 | + | |
297 | + if (currentP != null && lastP.distance(currentP) > EPS) { | |
298 | + | |
299 | + points.get(wiimote).put(state, currentP); | |
300 | + last.put(wiimote, currentP); | |
301 | + stepChange = true; | |
302 | + } | |
303 | + } | |
304 | + | |
305 | + // process at least 5 points to not miss a signal | |
306 | + if (stepChange && ++sc > 5) { | |
307 | + sc = 0; | |
308 | + stepChange = false; | |
309 | + CalibrationState current = state; | |
310 | + state = state.getNext(); | |
311 | + calibrationFrame.finished(current); | |
312 | + | |
313 | + if (checkPoints || current == CalibrationState.REGULAR_END) { | |
314 | + checkPoints = true; | |
315 | + if (calculateQuadsForWiimotes()) { | |
316 | + // OK, calibration finished | |
317 | + state = CalibrationState.DONE; | |
318 | + calculateTransformation(); | |
319 | + if (screen.getFullScreenWindow() == calibrationFrame) | |
320 | + screen.setFullScreenWindow(null); | |
321 | + calibrationFrame.setVisible(false); | |
322 | + notifyListener(CalibrationEvent.FINISHED); | |
323 | + return true; | |
324 | + } else { | |
325 | + // Go on | |
326 | + } | |
327 | + } | |
328 | + } | |
329 | + | |
330 | + if (state == CalibrationState.DONE) { | |
331 | + // PROBLEM, no solution found | |
332 | + state = CalibrationState.PENDING; | |
333 | + if (screen.getFullScreenWindow() == calibrationFrame) | |
334 | + screen.setFullScreenWindow(null); | |
335 | + calibrationFrame.setVisible(false); | |
336 | + notifyListener(CalibrationEvent.ABORTED); | |
337 | + new Thread(new Runnable() { | |
338 | + public void run() { | |
339 | + JOptionPane.showMessageDialog(null, Util.getResourceMap(WiimoteCalibration.class).getString("coverageError"), Util.getResourceMap(WiimoteCalibration.class).getString("calibrationFailed"), JOptionPane.ERROR_MESSAGE); | |
340 | + } | |
341 | + }).start(); | |
342 | + } | |
343 | + | |
344 | + return true; | |
345 | + } | |
346 | + | |
347 | + return false; | |
348 | + } | |
349 | + | |
350 | + private static boolean checkExist(Map<CalibrationState, Point2D> p, CalibrationState... states) { | |
351 | + for (CalibrationState s : states) { | |
352 | + if (p.get(s) == null) | |
353 | + return false; | |
354 | + } | |
355 | + return true; | |
356 | + } | |
357 | + | |
358 | + private boolean calculateQuadsForWiimotes() { | |
359 | + boolean success = true; | |
360 | + Set<CalibrationState> check = new HashSet<CalibrationState>(); | |
361 | + | |
362 | + for (Wiimote wiimote : points.keySet()) { | |
363 | + boolean ok = false; | |
364 | + | |
365 | + for (int i = 0; i < VALID_STATES.length; i++) { | |
366 | + if (checkExist(points.get(wiimote), VALID_STATES[i])) { | |
367 | + Map<CalibrationState, Point2D> calibrated = points.get(wiimote); | |
368 | + CalibrationState[] states = VALID_STATES[i]; | |
369 | + check.addAll(Arrays.asList(states)); | |
370 | + finals.put(wiimote.getAddress(), new Double[] { | |
371 | + calibrated.get(states[0]).getX(), calibrated.get(states[0]).getY(), | |
372 | + calibrated.get(states[1]).getX(), calibrated.get(states[1]).getY(), | |
373 | + calibrated.get(states[2]).getX(), calibrated.get(states[2]).getY(), | |
374 | + calibrated.get(states[3]).getX(), calibrated.get(states[3]).getY(), | |
375 | + (double)states[0].getX(bounds), (double)states[0].getY(bounds), | |
376 | + (double)states[1].getX(bounds), (double)states[1].getY(bounds), | |
377 | + (double)states[2].getX(bounds), (double)states[2].getY(bounds), | |
378 | + (double)states[3].getX(bounds), (double)states[3].getY(bounds) | |
379 | + }); | |
380 | + ok = true; | |
381 | + break; | |
382 | + } | |
383 | + } | |
384 | + | |
385 | + success = success && ok; | |
386 | + } | |
387 | + // check also if the whole screen is covered | |
388 | + return success && check.containsAll(Arrays.asList(VALID_STATES[0])); | |
389 | + } | |
390 | + | |
391 | + private void calculateTransformation() { | |
392 | + transformer.clear(); | |
393 | + for (String address : finals.keySet()) { | |
394 | + Double[] d = finals.get(address); | |
395 | + transformer.put(address, PerspectiveTransform.getQuadToQuad(d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7], d[8], d[9], d[10], d[11], d[12], d[13], d[14], d[15])); | |
396 | + } | |
397 | + } | |
398 | + | |
399 | + public Map<String, PerspectiveTransform> getTransformer() { | |
400 | + return transformer; | |
401 | + } | |
402 | + | |
403 | + public IRDot warp(int i, Wiimote wiimote, Map<Wiimote, IRDot[]> data) { | |
404 | + final PerspectiveTransform transform = transformer.get(wiimote.getAddress()); | |
405 | + if (transform == null || data.get(wiimote)[i] == null) return null; | |
406 | + else { | |
407 | + return (IRDot) transform.transform(data.get(wiimote)[i], new IRDot(data.get(wiimote)[i])); | |
408 | + } | |
409 | + } | |
410 | + | |
411 | + private Map<Wiimote, IRDot[]> warpIndividually(Map<Wiimote, IRDot[]> data) { | |
412 | + Map<Wiimote, IRDot[]> warped = new LinkedHashMap<Wiimote, IRDot[]>(); | |
413 | + IRDot[] p; | |
414 | + for (Wiimote wiimote : data.keySet()) { | |
415 | + warped.put(wiimote, p = new IRDot[4]); | |
416 | + for (int i = 0; i < 4; i++) { | |
417 | + p[i] = warp(i, wiimote, data); | |
418 | + } | |
419 | + } | |
420 | + return warped; | |
421 | + } | |
422 | + | |
423 | + public Point[] warp(Map<Wiimote, IRDot[]> data) { | |
424 | + Point[] warped = new Point[4]; | |
425 | + if (isDone() && isAnyCalibrated(data.keySet())) { | |
426 | + IRDot[][] cluster = PointClusterer.cluster(warpIndividually(data)); | |
427 | + | |
428 | + for (int i = 0; i < cluster.length; i++) { | |
429 | + if (cluster[i] == null) break; | |
430 | + double x = 0, y = 0; | |
431 | + int c = 0; | |
432 | + for (IRDot p : cluster[i]) { | |
433 | + x += p.getX(); | |
434 | + y += p.getY(); | |
435 | + c++; | |
436 | + break; | |
437 | + } | |
438 | + warped[i] = new Point((int) Math.round(x / c), (int) Math.round(y / c)); | |
439 | + } | |
440 | + } else { | |
441 | + System.err.println("Not calibrated."); | |
442 | + } | |
443 | + return warped; | |
444 | + } | |
445 | + | |
446 | + | |
447 | + public boolean load(InputStream is) throws IOException { | |
448 | + try { | |
449 | + BufferedReader in = new BufferedReader(new InputStreamReader(is)); | |
450 | + String address = null; | |
451 | + | |
452 | + while ((address = in.readLine()) != null) { | |
453 | + Double[] d = new Double[16]; | |
454 | + finals.put(address, d); | |
455 | + for (int i = 0; i < 4; i++) { | |
456 | + String[] p = in.readLine().split(" "); | |
457 | + for (int j = 0; j < 2; j++) { | |
458 | + d[i*2+j+0] = Double.valueOf(p[j+0]); | |
459 | + d[i*2+j+8] = Double.valueOf(p[j+2]); | |
460 | + } | |
461 | + } | |
462 | + } | |
463 | + | |
464 | + calculateTransformation(); | |
465 | + state = CalibrationState.DONE; | |
466 | + notifyListener(CalibrationEvent.LOADED); | |
467 | + return true; | |
468 | + | |
469 | + } catch (Exception e) { | |
470 | + e.printStackTrace(); | |
471 | + return false; | |
472 | + } | |
473 | + } | |
474 | + | |
475 | + public void save(OutputStream os) throws IOException { | |
476 | + PrintStream out = new PrintStream(os); | |
477 | + | |
478 | + for (String address : finals.keySet()) { | |
479 | + out.print(address); | |
480 | + Double[] d = finals.get(address); | |
481 | + for (int i = 0; i < 4; i++) { | |
482 | + out.printf(Locale.ENGLISH, "\n%f %f %.0f %.0f", d[i*2+0+0], d[i*2+1+0], d[i*2+0+8], d[i*2+1+8]); | |
483 | + } | |
484 | + out.println(); | |
485 | + } | |
486 | + out.close(); | |
487 | + | |
488 | + notifyListener(CalibrationEvent.SAVED); | |
489 | + } | |
490 | + | |
491 | + | |
492 | + /* | |
493 | + * LISTENER STUFF | |
494 | + */ | |
495 | + | |
496 | + public void addCalibrationEventListener(CalibrationEventListener l) { | |
497 | + listener.add(l); | |
498 | + } | |
499 | + | |
500 | + public void removeCalibrationEventListener(CalibrationEventListener l) { | |
501 | + listener.remove(l); | |
502 | + } | |
503 | + | |
504 | + private void notifyListener(CalibrationEvent e) { | |
505 | + WiimoteWhiteboard.getLogger().info("Calibration Event: " + e); | |
506 | + for (CalibrationEventListener l : listener) { | |
507 | + l.calibrationEvent(e); | |
508 | + } | |
509 | + } | |
510 | + | |
511 | +} | ... | ... |
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/calibration/resources/WiimoteCalibration.properties
0 → 100644
... | ... | @@ -0,0 +1,4 @@ |
1 | +infoLabel.text = <html><center>Press ESC to abort calibration.<br><br>Make sure all connected Wiimotes can see the screen.</center></html> | |
2 | + | |
3 | +calibrationFailed = Calibration failed | |
4 | +coverageError = The Wiimotes don't seem to cover the whole screen.${nl}Try to reposition them and start again. | ... | ... |
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/calibration/resources/WiimoteCalibration_de.properties
0 → 100644
... | ... | @@ -0,0 +1,8 @@ |
1 | +#:1265226421,Uwe | |
2 | + | |
3 | +infoLabel.text=<html><center>Dr\u00fccke ESC um die Kalibrierung abzubrechen.<br><br>Achte darauf dass alle Wiimotes den Bildschirm gut sehen k\u00f6nnen.</center></html> | |
4 | + | |
5 | +calibrationFailed=Kalibrierung fehlgeschlagen | |
6 | + | |
7 | +coverageError=Die Wiimotes scheinen nicht den gesamten Bildschirm abzudecken.${nl}Versuche sie neu zu positionieren und starte dann nochmal. | |
8 | + | ... | ... |
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/calibration/resources/WiimoteCalibration_es.properties
0 → 100644
... | ... | @@ -0,0 +1,8 @@ |
1 | +#:1224248803,Uwe | |
2 | + | |
3 | +infoLabel.text=<html><center>Oprime ESC para cancelar calibraci\u00f3n.<br><br>Aseg\u00farese de que todos los Wiimotes puedan ver la pantalla.</center></html> | |
4 | + | |
5 | +calibrationFailed=La calibraci\u00f3n fallo | |
6 | + | |
7 | +coverageError=Los Wiimotes no parecen cubrir la pantalla entera.${nl}Cambien la posici\u00f3n de los Wiimotes y reinicie la calibraci\u00f3n. | |
8 | + | ... | ... |
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/calibration/resources/WiimoteCalibration_et.properties
0 → 100644
... | ... | @@ -0,0 +1,8 @@ |
1 | +#:1248454359,Marko | |
2 | + | |
3 | +infoLabel.text=<html><center>Vajuta ESC klahvi kui soovid kalibreerimist katkestada.<br><br>Veendu, et k\u00f5ik Wii puldid n\u00e4evad kogu ekraani.</center></html> | |
4 | + | |
5 | +calibrationFailed=Kalibreerimine eba\u00f5nnestus | |
6 | + | |
7 | +coverageError=K\u00f5ik Wii puldid ei n\u00e4e kogu ekraani.${nl}Proovi pultide asendit korrigeerida ning alusta kalibreerimist uuesti. | |
8 | + | ... | ... |
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/calibration/resources/WiimoteCalibration_fr.properties
0 → 100644
... | ... | @@ -0,0 +1,8 @@ |
1 | +#:1265226429,Uwe | |
2 | + | |
3 | +infoLabel.text=<html><center>Presser ESC pour annuler la calibration.<br><br>V\u00e9rifier que toutes les Wiimotes connect\u00e9s peuvent capter l'\u00e9cran.</center></html> | |
4 | + | |
5 | +calibrationFailed=La calibration a \u00e9chou\u00e9 | |
6 | + | |
7 | +coverageError=Les Wiimotes ne semblent pas capter la totalit\u00e9 de l'\u00e9cran.${nl}Essayer de les placer mieux et red\u00e9marrer la calibration. | |
8 | + | ... | ... |
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/calibration/resources/WiimoteCalibration_in.properties
0 → 100644
... | ... | @@ -0,0 +1,8 @@ |
1 | +#:1274618479,Bobby | |
2 | + | |
3 | +infoLabel.text=<html><center>Tekan ESC untuk membatalkan Kalibrasi.<br><br>Pastikan semua Wiimotes yang terhubung dapat melihat layar.</center></html> | |
4 | + | |
5 | +calibrationFailed=Kalibrasi gagal | |
6 | + | |
7 | +coverageError=Wiimotes nampaknya tidak dapat melihat keseluruhan layar.${nl}Coba ubah posisinya dan mulai lagi dari awal. | |
8 | + | ... | ... |
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/calibration/resources/WiimoteCalibration_it.properties
0 → 100644
... | ... | @@ -0,0 +1,8 @@ |
1 | +#:1240167662,Uwe | |
2 | + | |
3 | +infoLabel.text=<html><center>Premi ESC per interrompere la calibrazione.<br><br>Assicurati che tutti i Wiimote connessi possano "vedere" lo schermo.</center></html> | |
4 | + | |
5 | +calibrationFailed=Calibrazione fallita | |
6 | + | |
7 | +coverageError=Il Wiimote non sembra coprire l'intero schermo.${nl}Prova a riposizionarlo, e ritenta. | |
8 | + | ... | ... |
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/calibration/resources/WiimoteCalibration_pl.properties
0 → 100644
... | ... | @@ -0,0 +1,9 @@ |
1 | +#:1253207960,Grzegorz | |
2 | + | |
3 | +# Wiimote = Wiilot in Polish | |
4 | +infoLabel.text=<html><center>Naci\u015bnij ESC aby przerwa\u0107 kalibracj\u0119.<br><br>Sprawd\u017a czy wszystkie pod\u0142\u0105czone Wiiloty widz\u0105 ekran.</center></html> | |
5 | + | |
6 | +calibrationFailed=Kalibracja nie powiod\u0142a si\u0119 | |
7 | + | |
8 | +coverageError=Wiiloty nie obejmuj\u0105 ca\u0142ego ekranu. Zmie\u0144 ich pozycj\u0119 i spr\u00f3buj ponownie. | |
9 | + | ... | ... |
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/calibration/resources/WiimoteCalibration_pt.properties
0 → 100644
... | ... | @@ -0,0 +1,8 @@ |
1 | +#:1224248742,Uwe | |
2 | + | |
3 | +infoLabel.text=<html><center>Pressione ESC para abortar a calibra\u00e7\u00e3o.<br><br>Certifique-se que todos os Wiimotes conseguem ver o ecr\u00e3.</center></html> | |
4 | + | |
5 | +calibrationFailed=Calibra\u00e7\u00e3o n\u00e3o efectuada | |
6 | + | |
7 | +coverageError=Os Wiimotes parecem n\u00e3o abranger toda a \u00e1rea do ecr\u00e3.${nl}Tente outra posi\u00e7\u00e3o e recomece. | |
8 | + | ... | ... |
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/calibration/resources/WiimoteCalibration_ru.properties
0 → 100644
... | ... | @@ -0,0 +1,8 @@ |
1 | +#:1265227045,Uwe | |
2 | + | |
3 | +infoLabel.text=<html><center>\u041d\u0430\u0436\u043c\u0438\u0442\u0435 ESC \u0447\u0442\u043e\u0431\u044b \u043f\u0440\u0435\u0440\u0432\u0430\u0442\u044c \u043a\u0430\u043b\u0438\u0431\u0440\u043e\u0432\u043a\u0443.<br><br>\u0423\u0431\u0435\u0434\u0438\u0442\u0435\u0441\u044c, \u0447\u0442\u043e \u0432\u0441\u0435 \u043f\u0440\u0438\u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u043d\u044b\u0435 Wiimot'\u044b \u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u044b \u043d\u0430 \u044d\u043a\u0440\u0430\u043d.</center></html> | |
4 | + | |
5 | +calibrationFailed=\u041a\u0430\u043b\u0438\u0431\u0440\u043e\u0432\u043a\u0430 \u043d\u0435 \u0443\u0434\u0430\u043b\u0430\u0441\u044c | |
6 | + | |
7 | +coverageError=Wiimote \u043d\u0435 \u0432\u0438\u0434\u0438\u0442 \u0432\u0435\u0441\u044c \u044d\u043a\u0440\u0430\u043d \u0446\u0435\u043b\u0438\u043a\u043e\u043c.${nl}\u041f\u043e\u043f\u0440\u043e\u0431\u0443\u0439\u0442\u0435 \u043f\u0435\u0440\u0435\u043c\u0435\u0441\u0442\u0438\u0442\u044c \u0435\u0433\u043e \u0438 \u043d\u0430\u0447\u043d\u0438\u0442\u0435 \u043a\u0430\u043b\u0438\u0431\u0440\u043e\u0432\u043a\u0443 \u0441\u043d\u043e\u0432\u0430. | |
8 | + | ... | ... |
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/calibration/resources/WiimoteCalibration_sl.properties
0 → 100644
... | ... | @@ -0,0 +1,8 @@ |
1 | +#:1238601406,Samo | |
2 | + | |
3 | +infoLabel.text=<html><center>\u010ce \u017eelite prekiniti kalibracijo, pritisnite tipko ESC.<br><br>Poskrbite, da bodo vsi priklu\u010deni Wiimoti videli ekran ali projekcijsko povr\u0161ino.</center></html> | |
4 | + | |
5 | +calibrationFailed=Povezava ni uspela | |
6 | + | |
7 | +coverageError=Senzorji Wiimotov ne zaznavajo celotnega ekrana ali projekcijske povr\u0161ine. ${nl}Postavite jih druga\u010de in poskusite znova. | |
8 | + | ... | ... |
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/calibration/resources/icons/francisco-crosshair.png
0 → 100644
3.54 KB
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/calibration/resources/icons/francisco-ok.png
0 → 100644
4.32 KB
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/calibration/resources/icons/francisco-warning.png
0 → 100644
3.66 KB
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/calibration/resources/icons/ok.png
0 → 100644
1019 Bytes
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/calibration/resources/icons/warning.png
0 → 100644
1.19 KB
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/ds/IRDot.java
0 → 100644
... | ... | @@ -0,0 +1,47 @@ |
1 | +package org.mote.wiimote.whiteboard.ds; | |
2 | + | |
3 | +import java.awt.geom.Point2D; | |
4 | + | |
5 | +import wiiremotej.IRLight; | |
6 | + | |
7 | +public class IRDot extends Point2D.Double { | |
8 | + | |
9 | + private final int id; | |
10 | + private double size; | |
11 | + | |
12 | + public IRDot(int id, double x, double y, double size) { | |
13 | + super(x, y); | |
14 | + this.id = id; | |
15 | + this.size = size; | |
16 | + } | |
17 | + | |
18 | + public IRDot(IRDot dot) { | |
19 | + this(dot.getId(), dot.getX(), dot.getY(), dot.getSize()); | |
20 | + } | |
21 | + | |
22 | + public IRDot(int id, IRLight light) { | |
23 | + this(id, light.getX(), light.getY(), light.getSize()); | |
24 | + } | |
25 | + | |
26 | + @Override | |
27 | + public String toString() { | |
28 | + return String.format("[Dot%d: x = %.2f, y = %.2f, s = %.2f]", id, x, y, size); | |
29 | + } | |
30 | + | |
31 | + public double getSize() { | |
32 | + return size; | |
33 | + } | |
34 | + | |
35 | + public int getId() { | |
36 | + return id; | |
37 | + } | |
38 | + | |
39 | + public static IRDot[] getIRDots(IRLight[] lights) { | |
40 | + IRDot[] dots = new IRDot[lights.length]; | |
41 | + for (int i = 0; i < lights.length; i++) { | |
42 | + dots[i] = lights[i] == null ? null : new IRDot(i, lights[i]); | |
43 | + } | |
44 | + return dots; | |
45 | + } | |
46 | + | |
47 | +} | ... | ... |
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/ds/IRDot.java~
0 → 100644
... | ... | @@ -0,0 +1,72 @@ |
1 | +/* | |
2 | + * Copyright (C) 2008-2009, Uwe Schmidt | |
3 | + * | |
4 | + * Permission is hereby granted, free of charge, to any person obtaining a | |
5 | + * copy of this software and associated documentation files (the "Software"), | |
6 | + * to deal in the Software without restriction, including without limitation | |
7 | + * the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
8 | + * and/or sell copies of the Software, and to permit persons to whom the | |
9 | + * Software is furnished to do so, subject to the following conditions: | |
10 | + * | |
11 | + * The above copyright notice and this permission notice shall be included in | |
12 | + * all copies or substantial portions of the Software. | |
13 | + * | |
14 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
15 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
16 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
17 | + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
18 | + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |
19 | + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | |
20 | + * DEALINGS IN THE SOFTWARE. | |
21 | + * | |
22 | + * The Software uses a third-party library (WiiRemoteJ) which is not part of | |
23 | + * the Software and is subject to its own license. | |
24 | + */ | |
25 | + | |
26 | +package org.uweschmidt.wiimote.whiteboard.ds; | |
27 | + | |
28 | +import java.awt.geom.Point2D; | |
29 | + | |
30 | +import wiiremotej.IRLight; | |
31 | + | |
32 | +public class IRDot extends Point2D.Double { | |
33 | + | |
34 | + private final int id; | |
35 | + private double size; | |
36 | + | |
37 | + public IRDot(int id, double x, double y, double size) { | |
38 | + super(x, y); | |
39 | + this.id = id; | |
40 | + this.size = size; | |
41 | + } | |
42 | + | |
43 | + public IRDot(IRDot dot) { | |
44 | + this(dot.getId(), dot.getX(), dot.getY(), dot.getSize()); | |
45 | + } | |
46 | + | |
47 | + public IRDot(int id, IRLight light) { | |
48 | + this(id, light.getX(), light.getY(), light.getSize()); | |
49 | + } | |
50 | + | |
51 | + @Override | |
52 | + public String toString() { | |
53 | + return String.format("[Dot%d: x = %.2f, y = %.2f, s = %.2f]", id, x, y, size); | |
54 | + } | |
55 | + | |
56 | + public double getSize() { | |
57 | + return size; | |
58 | + } | |
59 | + | |
60 | + public int getId() { | |
61 | + return id; | |
62 | + } | |
63 | + | |
64 | + public static IRDot[] getIRDots(IRLight[] lights) { | |
65 | + IRDot[] dots = new IRDot[lights.length]; | |
66 | + for (int i = 0; i < lights.length; i++) { | |
67 | + dots[i] = lights[i] == null ? null : new IRDot(i, lights[i]); | |
68 | + } | |
69 | + return dots; | |
70 | + } | |
71 | + | |
72 | +} | ... | ... |
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/ds/Wiimote.java
0 → 100644
... | ... | @@ -0,0 +1,23 @@ |
1 | +package org.mote.wiimote.whiteboard.ds; | |
2 | + | |
3 | +import wiiremotej.WiiRemote; | |
4 | + | |
5 | +public class Wiimote { | |
6 | + private final WiiRemote wiiremote; | |
7 | + private final String address; | |
8 | + private final int id; | |
9 | + public Wiimote(WiiRemote wiiremote, String address, int id) { | |
10 | + this.wiiremote = wiiremote; | |
11 | + this.address = address; | |
12 | + this.id = id; | |
13 | + } | |
14 | + public WiiRemote getWiiRemote() { | |
15 | + return wiiremote; | |
16 | + } | |
17 | + public String getAddress() { | |
18 | + return address; | |
19 | + } | |
20 | + public int getId() { | |
21 | + return id; | |
22 | + } | |
23 | +} | ... | ... |
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/ds/Wiimote.java~
0 → 100644
... | ... | @@ -0,0 +1,48 @@ |
1 | +/* | |
2 | + * Copyright (C) 2008-2009, Uwe Schmidt | |
3 | + * | |
4 | + * Permission is hereby granted, free of charge, to any person obtaining a | |
5 | + * copy of this software and associated documentation files (the "Software"), | |
6 | + * to deal in the Software without restriction, including without limitation | |
7 | + * the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
8 | + * and/or sell copies of the Software, and to permit persons to whom the | |
9 | + * Software is furnished to do so, subject to the following conditions: | |
10 | + * | |
11 | + * The above copyright notice and this permission notice shall be included in | |
12 | + * all copies or substantial portions of the Software. | |
13 | + * | |
14 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
15 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
16 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
17 | + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
18 | + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |
19 | + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | |
20 | + * DEALINGS IN THE SOFTWARE. | |
21 | + * | |
22 | + * The Software uses a third-party library (WiiRemoteJ) which is not part of | |
23 | + * the Software and is subject to its own license. | |
24 | + */ | |
25 | + | |
26 | +package org.uweschmidt.wiimote.whiteboard.ds; | |
27 | + | |
28 | +import wiiremotej.WiiRemote; | |
29 | + | |
30 | +public class Wiimote { | |
31 | + private final WiiRemote wiiremote; | |
32 | + private final String address; | |
33 | + private final int id; | |
34 | + public Wiimote(WiiRemote wiiremote, String address, int id) { | |
35 | + this.wiiremote = wiiremote; | |
36 | + this.address = address; | |
37 | + this.id = id; | |
38 | + } | |
39 | + public WiiRemote getWiiRemote() { | |
40 | + return wiiremote; | |
41 | + } | |
42 | + public String getAddress() { | |
43 | + return address; | |
44 | + } | |
45 | + public int getId() { | |
46 | + return id; | |
47 | + } | |
48 | +} | |
0 | 49 | \ No newline at end of file | ... | ... |
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/gui/AboutWindow.java
0 → 100644
... | ... | @@ -0,0 +1,72 @@ |
1 | +package org.mote.wiimote.whiteboard.gui; | |
2 | + | |
3 | +import java.awt.BorderLayout; | |
4 | +import java.awt.Font; | |
5 | +import java.net.URL; | |
6 | + | |
7 | +import javax.swing.JDialog; | |
8 | +import javax.swing.JEditorPane; | |
9 | +import javax.swing.JLabel; | |
10 | +import javax.swing.JPanel; | |
11 | +import javax.swing.JScrollPane; | |
12 | +import javax.swing.event.HyperlinkEvent; | |
13 | +import javax.swing.event.HyperlinkListener; | |
14 | + | |
15 | +import net.miginfocom.swing.MigLayout; | |
16 | + | |
17 | +import org.jdesktop.application.Action; | |
18 | +import org.jdesktop.application.Application; | |
19 | +import org.mote.wiimote.whiteboard.WiimoteWhiteboard; | |
20 | +import org.mote.wiimote.whiteboard.util.BareBonesBrowserLaunch; | |
21 | +import org.mote.wiimote.whiteboard.util.Util; | |
22 | + | |
23 | +@SuppressWarnings("serial") | |
24 | +public class AboutWindow extends JDialog implements HyperlinkListener { | |
25 | + | |
26 | + public AboutWindow() { | |
27 | + super(Application.getInstance(WiimoteWhiteboard.class).getMainFrame()); | |
28 | + if (!Util.INSIDE_APP_BUNDLE) { | |
29 | + setName("aboutWindow"); | |
30 | + setLayout(new BorderLayout()); | |
31 | + final JPanel appPane = Util.newComponent(JPanel.class, "appPane"); | |
32 | + add(appPane, BorderLayout.NORTH); | |
33 | + appPane.setLayout(new MigLayout("insets i", "[center|center]")); | |
34 | + | |
35 | + JLabel appLabel = new JLabel(String.format("%s %s", WiimoteWhiteboard.getProperty("id"), WiimoteWhiteboard.getProperty("version"))); | |
36 | + appLabel.setFont(appLabel.getFont().deriveFont(Font.BOLD, 14f)); | |
37 | + JLabel crLabel = new JLabel(Util.getResourceMap(AboutWindow.class).getString("copyRight", WiimoteWhiteboard.getProperty("year"), WiimoteWhiteboard.getProperty("author"))); | |
38 | + crLabel.setFont(appLabel.getFont().deriveFont(10f)); | |
39 | + | |
40 | + appPane.add(appLabel, "flowy, split 2"); | |
41 | + appPane.add(crLabel, ""); | |
42 | + appPane.add(new JLabel(Util.getResourceMap(WiimoteWhiteboard.class).getImageIcon("icon")), "flowx, wrap"); | |
43 | + | |
44 | + try { | |
45 | + | |
46 | + JEditorPane tp = Util.newComponent(JEditorPane.class, "infoPane"); | |
47 | + tp.addHyperlinkListener(this); | |
48 | + | |
49 | + add(new JScrollPane(tp), BorderLayout.CENTER); | |
50 | + | |
51 | + Util.getResourceMap(AboutWindow.class).injectComponents(this); | |
52 | + | |
53 | + setResizable(false); | |
54 | + Util.placeDialogWindow(this, 295, 348); | |
55 | + } catch (Exception e) { | |
56 | + e.printStackTrace(); | |
57 | + } | |
58 | + } | |
59 | + } | |
60 | + | |
61 | + public void hyperlinkUpdate(HyperlinkEvent e) { | |
62 | + if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) { | |
63 | + | |
64 | + } | |
65 | + } | |
66 | + | |
67 | + @Action | |
68 | + public void about() { | |
69 | + setVisible(true); | |
70 | + } | |
71 | + | |
72 | +} | ... | ... |
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/gui/AboutWindow.java~
0 → 100644
... | ... | @@ -0,0 +1,72 @@ |
1 | +package org.mote.wiimote.whiteboard.gui; | |
2 | + | |
3 | +import java.awt.BorderLayout; | |
4 | +import java.awt.Font; | |
5 | +import java.net.URL; | |
6 | + | |
7 | +import javax.swing.JDialog; | |
8 | +import javax.swing.JEditorPane; | |
9 | +import javax.swing.JLabel; | |
10 | +import javax.swing.JPanel; | |
11 | +import javax.swing.JScrollPane; | |
12 | +import javax.swing.event.HyperlinkEvent; | |
13 | +import javax.swing.event.HyperlinkListener; | |
14 | + | |
15 | +import net.miginfocom.swing.MigLayout; | |
16 | + | |
17 | +import org.jdesktop.application.Action; | |
18 | +import org.jdesktop.application.Application; | |
19 | +import org.mote.wiimote.whiteboard.WiimoteWhiteboard; | |
20 | +import org.mote.wiimote.whiteboard.util.BareBonesBrowserLaunch; | |
21 | +import org.mote.wiimote.whiteboard.util.Util; | |
22 | + | |
23 | +@SuppressWarnings("serial") | |
24 | +public class AboutWindow extends JDialog implements HyperlinkListener { | |
25 | + | |
26 | + public AboutWindow() { | |
27 | + super(Application.getInstance(WiimoteWhiteboard.class).getMainFrame()); | |
28 | + if (!Util.INSIDE_APP_BUNDLE) { | |
29 | + setName("aboutWindow"); | |
30 | + setLayout(new BorderLayout()); | |
31 | + final JPanel appPane = Util.newComponent(JPanel.class, "appPane"); | |
32 | + add(appPane, BorderLayout.NORTH); | |
33 | + appPane.setLayout(new MigLayout("insets i", "[center|center]")); | |
34 | + | |
35 | + JLabel appLabel = new JLabel(String.format("%s %s", WiimoteWhiteboard.getProperty("id"), WiimoteWhiteboard.getProperty("version"))); | |
36 | + appLabel.setFont(appLabel.getFont().deriveFont(Font.BOLD, 14f)); | |
37 | + JLabel crLabel = new JLabel(Util.getResourceMap(AboutWindow.class).getString("copyRight", WiimoteWhiteboard.getProperty("year"), WiimoteWhiteboard.getProperty("author"))); | |
38 | + crLabel.setFont(appLabel.getFont().deriveFont(10f)); | |
39 | + | |
40 | + appPane.add(appLabel, "flowy, split 2"); | |
41 | + appPane.add(crLabel, ""); | |
42 | + appPane.add(new JLabel(Util.getResourceMap(WiimoteWhiteboard.class).getImageIcon("icon")), "flowx, wrap"); | |
43 | + | |
44 | + try { | |
45 | + URL url = AboutWindow.class.getResource("resources/Credits.html"); | |
46 | + JEditorPane tp = Util.newComponent(JEditorPane.class, "infoPane"); | |
47 | + tp.addHyperlinkListener(this); | |
48 | + tp.setPage(url); | |
49 | + add(new JScrollPane(tp), BorderLayout.CENTER); | |
50 | + | |
51 | + Util.getResourceMap(AboutWindow.class).injectComponents(this); | |
52 | + | |
53 | + setResizable(false); | |
54 | + Util.placeDialogWindow(this, 295, 348); | |
55 | + } catch (Exception e) { | |
56 | + e.printStackTrace(); | |
57 | + } | |
58 | + } | |
59 | + } | |
60 | + | |
61 | + public void hyperlinkUpdate(HyperlinkEvent e) { | |
62 | + if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) { | |
63 | + BareBonesBrowserLaunch.openURL(e.getURL().toString()); | |
64 | + } | |
65 | + } | |
66 | + | |
67 | + @Action | |
68 | + public void about() { | |
69 | + setVisible(true); | |
70 | + } | |
71 | + | |
72 | +} | ... | ... |
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/gui/CalibrationInfoWindow.java
0 → 100644
... | ... | @@ -0,0 +1,267 @@ |
1 | +package org.mote.wiimote.whiteboard.gui; | |
2 | + | |
3 | +import java.awt.Color; | |
4 | +import java.awt.Graphics; | |
5 | +import java.awt.Graphics2D; | |
6 | +import java.awt.Point; | |
7 | +import java.awt.RenderingHints; | |
8 | +import java.awt.SystemColor; | |
9 | +import java.awt.event.ComponentAdapter; | |
10 | +import java.awt.event.ComponentEvent; | |
11 | +import java.awt.event.KeyAdapter; | |
12 | +import java.awt.event.KeyEvent; | |
13 | +import java.util.Map; | |
14 | +import java.util.Timer; | |
15 | +import java.util.TimerTask; | |
16 | + | |
17 | +import javax.swing.BorderFactory; | |
18 | +import javax.swing.JDialog; | |
19 | +import javax.swing.JLabel; | |
20 | +import javax.swing.JPanel; | |
21 | +import javax.swing.JProgressBar; | |
22 | + | |
23 | +import net.miginfocom.swing.MigLayout; | |
24 | + | |
25 | +import org.jdesktop.application.Action; | |
26 | +import org.jdesktop.application.Application; | |
27 | +import org.mote.wiimote.whiteboard.WiimoteDataHandler; | |
28 | +import org.mote.wiimote.whiteboard.WiimoteWhiteboard; | |
29 | +import org.mote.wiimote.whiteboard.WiimoteDataHandler.WiimoteDataListener; | |
30 | +import org.mote.wiimote.whiteboard.calibration.WiimoteCalibration; | |
31 | +import org.mote.wiimote.whiteboard.calibration.WiimoteCalibration.CalibrationEvent; | |
32 | +import org.mote.wiimote.whiteboard.calibration.WiimoteCalibration.CalibrationEventListener; | |
33 | +import org.mote.wiimote.whiteboard.ds.IRDot; | |
34 | +import org.mote.wiimote.whiteboard.ds.Wiimote; | |
35 | +import org.mote.wiimote.whiteboard.preferences.WWPreferences; | |
36 | +import org.mote.wiimote.whiteboard.util.Util; | |
37 | + | |
38 | +@SuppressWarnings("serial") | |
39 | +public class CalibrationInfoWindow extends JDialog implements CalibrationEventListener, WiimoteDataListener { | |
40 | + | |
41 | + private static class WiimoteWrapper { | |
42 | + @SuppressWarnings("unused") | |
43 | + private int idx; | |
44 | + private JPanel panel; | |
45 | + private WiimoteIcon icon; | |
46 | + private JProgressBar trackingUtility; | |
47 | + private Double d[]; | |
48 | + private IRDot[] lights = new IRDot[4]; | |
49 | + private void setVisible(boolean visible) { | |
50 | + panel.setVisible(visible); | |
51 | + icon.setVisible(visible); | |
52 | + trackingUtility.setVisible(visible); | |
53 | + } | |
54 | + } | |
55 | + | |
56 | + private static final long REPAINT_FREQ = 1000 / 25; | |
57 | + | |
58 | + private static final Color CALIBRATED_COLOR = SystemColor.textHighlight; | |
59 | + private static final Color TRACKING_COLOR = SystemColor.text; | |
60 | + | |
61 | + private static final Color COLORS[] = {Color.blue, Color.red, Color.green, Color.orange, Color.black, Color.cyan, Color.magenta, Color.pink}; | |
62 | + | |
63 | + private WiimoteWrapper ww[] = new WiimoteWrapper[WWPreferences.WIIMOTES]; | |
64 | + private WiimoteCalibration calibration; | |
65 | + private WiimoteDataHandler dh; | |
66 | + private boolean calibrated = false; | |
67 | + | |
68 | +// private Matrix F; | |
69 | + | |
70 | + | |
71 | + public CalibrationInfoWindow(WiimoteCalibration calibration, WiimoteDataHandler dh) { | |
72 | + super(Application.getInstance(WiimoteWhiteboard.class).getMainFrame(), Util.getResourceMap(CalibrationInfoWindow.class).getString("info.Action.text")); | |
73 | + getRootPane().putClientProperty("Window.style", "small"); | |
74 | + this.calibration = calibration; | |
75 | + this.dh = dh; | |
76 | + calibration.addCalibrationEventListener(this); | |
77 | + dh.addWiimoteDataListener(this); | |
78 | + setLayout(new MigLayout("hidemode 3")); | |
79 | + // info labels | |
80 | + final JLabel taLabel = Util.newComponent(JLabel.class, "trackingAreaLabel"); | |
81 | + final JLabel csLabel = Util.newComponent(JLabel.class, "calibratedScreenLabel"); | |
82 | + taLabel.setBorder(BorderFactory.createLineBorder(Color.lightGray)); | |
83 | + csLabel.setBorder(BorderFactory.createLineBorder(Color.lightGray)); | |
84 | + taLabel.setBackground(TRACKING_COLOR); | |
85 | + csLabel.setBackground(CALIBRATED_COLOR); | |
86 | + taLabel.setForeground(SystemColor.textText); | |
87 | + csLabel.setForeground(SystemColor.textHighlightText); | |
88 | + add(taLabel, "sg 1, split, span, growx, pushx"); | |
89 | + add(csLabel, "sg 1, gapbefore 0, growx, pushx, wrap"); | |
90 | + | |
91 | + for (int i = 0; i < ww.length; i++) { | |
92 | + ww[i] = new WiimoteWrapper(); | |
93 | + ww[i].idx = i; | |
94 | + ww[i].icon = new WiimoteIcon(i+1); | |
95 | + ww[i].icon.displayConnected(true); | |
96 | + add(ww[i].panel = new InfoPanel(ww[i]), "flowy, w 400, h 300, grow, push"); | |
97 | + add(ww[i].icon, "cell "+i+" 1, growx, pushx"); | |
98 | + add(ww[i].trackingUtility = Util.newComponent(JProgressBar.class, "trackingUtility"), "cell "+i+" 1, growx, pushx"); | |
99 | + ww[i].setVisible(i == 0); | |
100 | + } | |
101 | + | |
102 | + addKeyListener(new KeyAdapter() { | |
103 | + @Override | |
104 | + public void keyPressed(KeyEvent e) { | |
105 | + if (e.getKeyCode() == KeyEvent.VK_ESCAPE) { | |
106 | + setVisible(false); | |
107 | + } | |
108 | + } | |
109 | + }); | |
110 | + | |
111 | + addComponentListener(new ComponentAdapter() { | |
112 | + @Override | |
113 | + public void componentMoved(ComponentEvent e) { | |
114 | + componentResized(null); | |
115 | + } | |
116 | + @Override | |
117 | + public void componentResized(ComponentEvent e) { | |
118 | + repaintPanels(); | |
119 | + } | |
120 | + }); | |
121 | + | |
122 | + | |
123 | + Util.getResourceMap(CalibrationInfoWindow.class).injectComponents(this); | |
124 | + pack(); | |
125 | + Util.placeDialogWindow(this, getWidth(), getHeight()); | |
126 | + new Timer(true).schedule(new UpdateTask(), 0, REPAINT_FREQ); | |
127 | + } | |
128 | + | |
129 | + private class UpdateTask extends TimerTask { | |
130 | + @Override | |
131 | + public void run() { | |
132 | + if (isVisible()) { | |
133 | + repaintPanels(); | |
134 | + } | |
135 | + } | |
136 | + } | |
137 | + | |
138 | + private void repaintPanels() { | |
139 | + for (WiimoteWrapper wrapper : ww) { | |
140 | + if (wrapper.panel.isVisible()) | |
141 | + wrapper.panel.repaint(); | |
142 | + } | |
143 | + } | |
144 | + | |
145 | + @Action(enabledProperty="calibrated") | |
146 | + public void info() { | |
147 | + setVisible(true); | |
148 | + } | |
149 | + | |
150 | + public boolean isCalibrated() { | |
151 | + return calibrated; | |
152 | + } | |
153 | + | |
154 | + private void updateCalibrated() { | |
155 | + final boolean old = calibrated; | |
156 | + calibrated = dh.isConnected() && calibration.isDone() && calibration.isAnyCalibrated(dh.getConnectedWiimotes()); | |
157 | + firePropertyChange("calibrated", old, calibrated); | |
158 | + } | |
159 | + | |
160 | + public void calibrationEvent(CalibrationEvent e) { | |
161 | + switch (e) { | |
162 | + case LOADED: | |
163 | + update(); | |
164 | + case FINISHED: | |
165 | + update(); | |
166 | + break; | |
167 | + case SCREEN_CHANGED: | |
168 | + case STARTED: | |
169 | + case ABORTED: | |
170 | + case SAVED: | |
171 | + } | |
172 | + updateCalibrated(); | |
173 | + if (!isCalibrated()) | |
174 | + setVisible(false); | |
175 | + } | |
176 | + | |
177 | + public void batteryLevel(Wiimote wiimote, double level) {} | |
178 | + public void irLights(Wiimote wiimote, IRDot[] lights) { | |
179 | + ww[wiimote.getId()-1].lights = lights; | |
180 | + } | |
181 | + public void irWarped(Map<Wiimote, IRDot[]> data, Point[] warped) {} | |
182 | + public void wiimoteConnected(Wiimote wiimote) { | |
183 | + update(); | |
184 | + updateCalibrated(); | |
185 | + } | |
186 | + public void wiimoteDisconnected(Wiimote wiimote) {} | |
187 | + | |
188 | + private int r(double d) { | |
189 | + return (int)Math.round(d); | |
190 | + } | |
191 | + | |
192 | + private void update() { | |
193 | + Map<String, Double[]> finals = calibration.getFinals(); | |
194 | + | |
195 | + | |
196 | + | |
197 | + for (Wiimote wiimote : dh.getConnectedWiimotes()) { | |
198 | + WiimoteWrapper w = ww[wiimote.getId()-1]; | |
199 | + if (!calibration.isCalibrated(wiimote)) continue; | |
200 | + | |
201 | + w.d = finals.get(wiimote.getAddress()); | |
202 | + | |
203 | + double x0 = w.d[0]*1024, y0 = w.d[1]*768; | |
204 | + double x1 = w.d[2]*1024, y1 = w.d[3]*768; | |
205 | + double x3 = w.d[4]*1024, y3 = w.d[5]*768; | |
206 | + double x2 = w.d[6]*1024, y2 = w.d[7]*768; | |
207 | + double idealArea = 0.8 * 1024 * 0.8 * 768; | |
208 | + double calibratedArea = 0.5 * (Math.abs( | |
209 | + (x1 - x2) * (y0 - y3) - (x0 - x3) * (y1 - y2) | |
210 | + )); | |
211 | + | |
212 | + w.trackingUtility.setValue(r(calibratedArea)); | |
213 | + w.trackingUtility.setString(Util.getResourceMap(CalibrationInfoWindow.class).getString("trackingUtilString", 100 * (calibratedArea / idealArea))); | |
214 | + | |
215 | + if (!w.panel.isVisible()) { | |
216 | + w.setVisible(true); | |
217 | + pack(); | |
218 | + Util.placeDialogWindow(this, getWidth(), getHeight()); | |
219 | + } | |
220 | + } | |
221 | + repaintPanels(); | |
222 | + } | |
223 | + | |
224 | + private class InfoPanel extends JPanel { | |
225 | + private static final int DIAMETER = 6; | |
226 | + private WiimoteWrapper wrapper; | |
227 | + public InfoPanel(WiimoteWrapper wrapper) { | |
228 | + this.wrapper = wrapper; | |
229 | + setLayout(null); | |
230 | + setOpaque(true); | |
231 | + setBackground(TRACKING_COLOR); | |
232 | + setBorder(BorderFactory.createLineBorder(SystemColor.lightGray)); | |
233 | + } | |
234 | + @Override | |
235 | + protected void paintComponent(Graphics g) { | |
236 | + super.paintComponent(g); | |
237 | + if (wrapper.d != null) { | |
238 | + Graphics2D g2d = (Graphics2D)g; | |
239 | + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); | |
240 | + final int w = getWidth(); | |
241 | + final int h = getHeight(); | |
242 | + int x[] = new int[5], y[] = new int[5]; | |
243 | + for (int i = 0; i < 4; i++) { | |
244 | + x[i] = r(wrapper.d[2*i]*w); | |
245 | + y[i] = r(h-wrapper.d[2*i+1]*h); | |
246 | + } | |
247 | + x[4] = x[0]; y[4] = y[0]; | |
248 | + | |
249 | + g2d.setColor(CALIBRATED_COLOR); | |
250 | + g2d.fillPolygon(x, y, 5); | |
251 | + | |
252 | + | |
253 | + for (int i = 0; i < 4; i++) { | |
254 | + IRDot dot = wrapper.lights[i]; | |
255 | + if (dot != null) { | |
256 | + g2d.setColor(COLORS[i]); | |
257 | + | |
258 | + g2d.fillOval(r(w*dot.x)-DIAMETER/2, r(h-h*dot.y)-DIAMETER/2, DIAMETER, DIAMETER); | |
259 | + | |
260 | + } | |
261 | + } | |
262 | + } | |
263 | + } | |
264 | + } | |
265 | + | |
266 | + | |
267 | +} | ... | ... |
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/gui/CalibrationInfoWindow.java~
0 → 100644
... | ... | @@ -0,0 +1,267 @@ |
1 | +package org.mote.wiimote.whiteboard.gui; | |
2 | + | |
3 | +import java.awt.Color; | |
4 | +import java.awt.Graphics; | |
5 | +import java.awt.Graphics2D; | |
6 | +import java.awt.Point; | |
7 | +import java.awt.RenderingHints; | |
8 | +import java.awt.SystemColor; | |
9 | +import java.awt.event.ComponentAdapter; | |
10 | +import java.awt.event.ComponentEvent; | |
11 | +import java.awt.event.KeyAdapter; | |
12 | +import java.awt.event.KeyEvent; | |
13 | +import java.util.Map; | |
14 | +import java.util.Timer; | |
15 | +import java.util.TimerTask; | |
16 | + | |
17 | +import javax.swing.BorderFactory; | |
18 | +import javax.swing.JDialog; | |
19 | +import javax.swing.JLabel; | |
20 | +import javax.swing.JPanel; | |
21 | +import javax.swing.JProgressBar; | |
22 | + | |
23 | +import net.miginfocom.swing.MigLayout; | |
24 | + | |
25 | +import org.jdesktop.application.Action; | |
26 | +import org.jdesktop.application.Application; | |
27 | +import org.mote.wiimote.whiteboard.WiimoteDataHandler; | |
28 | +import org.mote.wiimote.whiteboard.WiimoteWhiteboard; | |
29 | +import org.mote.wiimote.whiteboard.WiimoteDataHandler.WiimoteDataListener; | |
30 | +import org.mote.wiimote.whiteboard.calibration.WiimoteCalibration; | |
31 | +import org.mote.wiimote.whiteboard.calibration.WiimoteCalibration.CalibrationEvent; | |
32 | +import org.mote.wiimote.whiteboard.calibration.WiimoteCalibration.CalibrationEventListener; | |
33 | +import org.mote.wiimote.whiteboard.ds.IRDot; | |
34 | +import org.mote.wiimote.whiteboard.ds.Wiimote; | |
35 | +import org.mote.wiimote.whiteboard.preferences.WWPreferences; | |
36 | +import org.mote.wiimote.whiteboard.util.Util; | |
37 | + | |
38 | +@SuppressWarnings("serial") | |
39 | +public class CalibrationInfoWindow extends JDialog implements CalibrationEventListener, WiimoteDataListener { | |
40 | + | |
41 | + private static class WiimoteWrapper { | |
42 | + @SuppressWarnings("unused") | |
43 | + private int idx; | |
44 | + private JPanel panel; | |
45 | + private WiimoteIcon icon; | |
46 | + private JProgressBar trackingUtility; | |
47 | + private Double d[]; | |
48 | + private IRDot[] lights = new IRDot[4]; | |
49 | + private void setVisible(boolean visible) { | |
50 | + panel.setVisible(visible); | |
51 | + icon.setVisible(visible); | |
52 | + trackingUtility.setVisible(visible); | |
53 | + } | |
54 | + } | |
55 | + | |
56 | + private static final long REPAINT_FREQ = 1000 / 25; | |
57 | + | |
58 | + private static final Color CALIBRATED_COLOR = SystemColor.textHighlight; | |
59 | + private static final Color TRACKING_COLOR = SystemColor.text; | |
60 | + | |
61 | + private static final Color COLORS[] = {Color.blue, Color.red, Color.green, Color.orange, Color.black, Color.cyan, Color.magenta, Color.pink}; | |
62 | + | |
63 | + private WiimoteWrapper ww[] = new WiimoteWrapper[WWPreferences.WIIMOTES]; | |
64 | + private WiimoteCalibration calibration; | |
65 | + private WiimoteDataHandler dh; | |
66 | + private boolean calibrated = false; | |
67 | + | |
68 | +// private Matrix F; | |
69 | + | |
70 | + | |
71 | + public CalibrationInfoWindow(WiimoteCalibration calibration, WiimoteDataHandler dh) { | |
72 | + super(Application.getInstance(WiimoteWhiteboard.class).getMainFrame(), Util.getResourceMap(CalibrationInfoWindow.class).getString("info.Action.text")); | |
73 | + getRootPane().putClientProperty("Window.style", "small"); | |
74 | + this.calibration = calibration; | |
75 | + this.dh = dh; | |
76 | + calibration.addCalibrationEventListener(this); | |
77 | + dh.addWiimoteDataListener(this); | |
78 | + setLayout(new MigLayout("hidemode 3")); | |
79 | + // info labels | |
80 | + final JLabel taLabel = Util.newComponent(JLabel.class, "trackingAreaLabel"); | |
81 | + final JLabel csLabel = Util.newComponent(JLabel.class, "calibratedScreenLabel"); | |
82 | + taLabel.setBorder(BorderFactory.createLineBorder(Color.lightGray)); | |
83 | + csLabel.setBorder(BorderFactory.createLineBorder(Color.lightGray)); | |
84 | + taLabel.setBackground(TRACKING_COLOR); | |
85 | + csLabel.setBackground(CALIBRATED_COLOR); | |
86 | + taLabel.setForeground(SystemColor.textText); | |
87 | + csLabel.setForeground(SystemColor.textHighlightText); | |
88 | + add(taLabel, "sg 1, split, span, growx, pushx"); | |
89 | + add(csLabel, "sg 1, gapbefore 0, growx, pushx, wrap"); | |
90 | + | |
91 | + for (int i = 0; i < ww.length; i++) { | |
92 | + ww[i] = new WiimoteWrapper(); | |
93 | + ww[i].idx = i; | |
94 | + ww[i].icon = new WiimoteIcon(i+1); | |
95 | + ww[i].icon.displayConnected(true); | |
96 | + add(ww[i].panel = new InfoPanel(ww[i]), "flowy, w 400, h 300, grow, push"); | |
97 | + add(ww[i].icon, "cell "+i+" 1, growx, pushx"); | |
98 | + add(ww[i].trackingUtility = Util.newComponent(JProgressBar.class, "trackingUtility"), "cell "+i+" 1, growx, pushx"); | |
99 | + ww[i].setVisible(i == 0); | |
100 | + } | |
101 | + | |
102 | + addKeyListener(new KeyAdapter() { | |
103 | + @Override | |
104 | + public void keyPressed(KeyEvent e) { | |
105 | + if (e.getKeyCode() == KeyEvent.VK_ESCAPE) { | |
106 | + setVisible(false); | |
107 | + } | |
108 | + } | |
109 | + }); | |
110 | + | |
111 | + addComponentListener(new ComponentAdapter() { | |
112 | + @Override | |
113 | + public void componentMoved(ComponentEvent e) { | |
114 | + componentResized(null); | |
115 | + } | |
116 | + @Override | |
117 | + public void componentResized(ComponentEvent e) { | |
118 | + repaintPanels(); | |
119 | + } | |
120 | + }); | |
121 | + | |
122 | + | |
123 | + Util.getResourceMap(CalibrationInfoWindow.class).injectComponents(this); | |
124 | + pack(); | |
125 | + Util.placeDialogWindow(this, getWidth(), getHeight()); | |
126 | + new Timer(true).schedule(new UpdateTask(), 0, REPAINT_FREQ); | |
127 | + } | |
128 | + | |
129 | + private class UpdateTask extends TimerTask { | |
130 | + @Override | |
131 | + public void run() { | |
132 | + if (isVisible()) { | |
133 | + repaintPanels(); | |
134 | + } | |
135 | + } | |
136 | + } | |
137 | + | |
138 | + private void repaintPanels() { | |
139 | + for (WiimoteWrapper wrapper : ww) { | |
140 | + if (wrapper.panel.isVisible()) | |
141 | + wrapper.panel.repaint(); | |
142 | + } | |
143 | + } | |
144 | + | |
145 | + @Action(enabledProperty="calibrated") | |
146 | + public void info() { | |
147 | + setVisible(true); | |
148 | + } | |
149 | + | |
150 | + public boolean isCalibrated() { | |
151 | + return calibrated; | |
152 | + } | |
153 | + | |
154 | + private void updateCalibrated() { | |
155 | + final boolean old = calibrated; | |
156 | + calibrated = dh.isConnected() && calibration.isDone() && calibration.isAnyCalibrated(dh.getConnectedWiimotes()); | |
157 | + firePropertyChange("calibrated", old, calibrated); | |
158 | + } | |
159 | + | |
160 | + public void calibrationEvent(CalibrationEvent e) { | |
161 | + switch (e) { | |
162 | + case LOADED: | |
163 | + update(); | |
164 | + case FINISHED: | |
165 | + update(); | |
166 | + break; | |
167 | + case SCREEN_CHANGED: | |
168 | + case STARTED: | |
169 | + case ABORTED: | |
170 | + case SAVED: | |
171 | + } | |
172 | + updateCalibrated(); | |
173 | + if (!isCalibrated()) | |
174 | + setVisible(false); | |
175 | + } | |
176 | + | |
177 | + public void batteryLevel(Wiimote wiimote, double level) {} | |
178 | + public void irLights(Wiimote wiimote, IRDot[] lights) { | |
179 | + ww[wiimote.getId()-1].lights = lights; | |
180 | + } | |
181 | + public void irWarped(Map<Wiimote, IRDot[]> data, Point[] warped) {} | |
182 | + public void wiimoteConnected(Wiimote wiimote) { | |
183 | + update(); | |
184 | + updateCalibrated(); | |
185 | + } | |
186 | + public void wiimoteDisconnected(Wiimote wiimote) {} | |
187 | + | |
188 | + private int r(double d) { | |
189 | + return (int)Math.round(d); | |
190 | + } | |
191 | + | |
192 | + private void update() { | |
193 | + Map<String, Double[]> finals = calibration.getFinals(); | |
194 | + | |
195 | + | |
196 | + | |
197 | + for (Wiimote wiimote : dh.getConnectedWiimotes()) { | |
198 | + WiimoteWrapper w = ww[wiimote.getId()-1]; | |
199 | + if (!calibration.isCalibrated(wiimote)) continue; | |
200 | + | |
201 | + w.d = finals.get(wiimote.getAddress()); | |
202 | + | |
203 | + double x0 = w.d[0]*1024, y0 = w.d[1]*768; | |
204 | + double x1 = w.d[2]*1024, y1 = w.d[3]*768; | |
205 | + double x3 = w.d[4]*1024, y3 = w.d[5]*768; | |
206 | + double x2 = w.d[6]*1024, y2 = w.d[7]*768; | |
207 | + double idealArea = 0.8 * 1024 * 0.8 * 768; | |
208 | + double calibratedArea = 0.5 * (Math.abs( | |
209 | + (x1 - x2) * (y0 - y3) - (x0 - x3) * (y1 - y2) | |
210 | + )); | |
211 | + | |
212 | + w.trackingUtility.setValue(r(calibratedArea)); | |
213 | + w.trackingUtility.setString(Util.getResourceMap(CalibrationInfoWindow.class).getString("trackingUtilString", 100 * (calibratedArea / idealArea))); | |
214 | + | |
215 | + if (!w.panel.isVisible()) { | |
216 | + w.setVisible(true); | |
217 | + pack(); | |
218 | + Util.placeDialogWindow(this, getWidth(), getHeight()); | |
219 | + } | |
220 | + } | |
221 | + repaintPanels(); | |
222 | + } | |
223 | + | |
224 | + private class InfoPanel extends JPanel { | |
225 | + private static final int DIAMETER = 6; | |
226 | + private WiimoteWrapper wrapper; | |
227 | + public InfoPanel(WiimoteWrapper wrapper) { | |
228 | + this.wrapper = wrapper; | |
229 | + setLayout(null); | |
230 | + setOpaque(true); | |
231 | + setBackground(TRACKING_COLOR); | |
232 | + setBorder(BorderFactory.createLineBorder(SystemColor.lightGray)); | |
233 | + } | |
234 | + @Override | |
235 | + protected void paintComponent(Graphics g) { | |
236 | + super.paintComponent(g); | |
237 | + if (wrapper.d != null) { | |
238 | + Graphics2D g2d = (Graphics2D)g; | |
239 | + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); | |
240 | + final int w = getWidth(); | |
241 | + final int h = getHeight(); | |
242 | + int x[] = new int[5], y[] = new int[5]; | |
243 | + for (int i = 0; i < 4; i++) { | |
244 | + x[i] = r(wrapper.d[2*i]*w); | |
245 | + y[i] = r(h-wrapper.d[2*i+1]*h); | |
246 | + } | |
247 | + x[4] = x[0]; y[4] = y[0]; | |
248 | + | |
249 | + g2d.setColor(CALIBRATED_COLOR); | |
250 | + g2d.fillPolygon(x, y, 5); | |
251 | + | |
252 | + | |
253 | + for (int i = 0; i < 4; i++) { | |
254 | + IRDot dot = wrapper.lights[i]; | |
255 | + if (dot != null) { | |
256 | + g2d.setColor(COLORS[i]); | |
257 | + | |
258 | + g2d.fillOval(r(w*dot.x)-DIAMETER/2, r(h-h*dot.y)-DIAMETER/2, DIAMETER, DIAMETER); | |
259 | + | |
260 | + } | |
261 | + } | |
262 | + } | |
263 | + } | |
264 | + } | |
265 | + | |
266 | + | |
267 | +} | ... | ... |
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/gui/CameraMonitor.java
0 → 100644
... | ... | @@ -0,0 +1,103 @@ |
1 | +package org.mote.wiimote.whiteboard.gui; | |
2 | + | |
3 | +import java.awt.Point; | |
4 | +import java.awt.SystemColor; | |
5 | +import java.awt.event.KeyAdapter; | |
6 | +import java.awt.event.KeyEvent; | |
7 | +import java.util.Map; | |
8 | +import java.util.Timer; | |
9 | +import java.util.TimerTask; | |
10 | + | |
11 | +import javax.swing.BorderFactory; | |
12 | +import javax.swing.JDialog; | |
13 | +import javax.swing.JPanel; | |
14 | + | |
15 | +import net.miginfocom.swing.MigLayout; | |
16 | + | |
17 | +import org.jdesktop.application.Action; | |
18 | +import org.jdesktop.application.Application; | |
19 | +import org.mote.wiimote.whiteboard.WiimoteDataHandler; | |
20 | +import org.mote.wiimote.whiteboard.WiimoteWhiteboard; | |
21 | +import org.mote.wiimote.whiteboard.WiimoteDataHandler.WiimoteDataListener; | |
22 | +import org.mote.wiimote.whiteboard.ds.IRDot; | |
23 | +import org.mote.wiimote.whiteboard.ds.Wiimote; | |
24 | +import org.mote.wiimote.whiteboard.preferences.WWPreferences; | |
25 | +import org.mote.wiimote.whiteboard.util.Util; | |
26 | + | |
27 | +@SuppressWarnings("serial") | |
28 | +public class CameraMonitor extends JDialog implements WiimoteDataListener { | |
29 | + | |
30 | + private static final long REPAINT_FREQ = 1000 / 25; | |
31 | + | |
32 | + private JPanel canvas; | |
33 | + private IRDot[][] lights = new IRDot[WWPreferences.WIIMOTES+1][4]; | |
34 | + private LightLabel[][] labels = new LightLabel[WWPreferences.WIIMOTES+1][4]; | |
35 | + | |
36 | + public CameraMonitor(WiimoteDataHandler dh) { | |
37 | + super(Application.getInstance(WiimoteWhiteboard.class).getMainFrame(), Util.getResourceMap(CameraMonitor.class).getString("monitor.Action.text")); | |
38 | + getRootPane().putClientProperty("Window.style", "small"); | |
39 | + setLayout(new MigLayout()); | |
40 | + | |
41 | + dh.addWiimoteDataListener(this); | |
42 | + | |
43 | + canvas = new JPanel(null, true); | |
44 | + canvas.setOpaque(true); | |
45 | + canvas.setBorder(BorderFactory.createLineBorder(SystemColor.inactiveCaptionBorder)); | |
46 | + add(canvas, "w 50sp, h 50sp, grow, push"); | |
47 | + | |
48 | + addKeyListener(new KeyAdapter() { | |
49 | + @Override | |
50 | + public void keyPressed(KeyEvent e) { | |
51 | + if (e.getKeyCode() == KeyEvent.VK_ESCAPE) { | |
52 | + setVisible(false); | |
53 | + } | |
54 | + } | |
55 | + }); | |
56 | + | |
57 | + pack(); | |
58 | + setLocationRelativeTo(null); | |
59 | + | |
60 | + new Timer(true).schedule(new UpdateTask(), 0, REPAINT_FREQ); | |
61 | + } | |
62 | + | |
63 | + @Action | |
64 | + public void monitor() { | |
65 | + setVisible(true); | |
66 | + } | |
67 | + | |
68 | + private class UpdateTask extends TimerTask { | |
69 | + @Override | |
70 | + public void run() { | |
71 | + if (isVisible()) { | |
72 | + for (int i = 1; i <= WWPreferences.WIIMOTES; i++) | |
73 | + for (int j = 0; j < 4; j++) { | |
74 | + if (labels[i][j] != null) | |
75 | + labels[i][j].update(); | |
76 | + } | |
77 | + } | |
78 | + } | |
79 | + } | |
80 | + | |
81 | + public void irLights(Wiimote wiimote, IRDot[] lights) { | |
82 | + this.lights[wiimote.getId()] = lights; | |
83 | + } | |
84 | + | |
85 | + public void irWarped(Map<Wiimote, IRDot[]> data, Point[] warped) { | |
86 | + } | |
87 | + | |
88 | + public void batteryLevel(Wiimote wiimote, double level) { | |
89 | + } | |
90 | + | |
91 | + public void wiimoteConnected(Wiimote wiimote) { | |
92 | + for (int i = 0; i < 4; i++) | |
93 | + canvas.add(labels[wiimote.getId()][i] = new LightLabel(canvas, lights, wiimote.getId(), i + 1)); | |
94 | + } | |
95 | + | |
96 | + public void wiimoteDisconnected(Wiimote wiimote) { | |
97 | + for (int i = 0; i < 4; i++) { | |
98 | + canvas.remove(labels[wiimote.getId()][i]); | |
99 | + labels[wiimote.getId()][i] = null; | |
100 | + } | |
101 | + } | |
102 | + | |
103 | +} | ... | ... |
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/gui/CameraMonitor.java~
0 → 100644
... | ... | @@ -0,0 +1,128 @@ |
1 | +/* | |
2 | + * Copyright (C) 2008-2009, Uwe Schmidt | |
3 | + * | |
4 | + * Permission is hereby granted, free of charge, to any person obtaining a | |
5 | + * copy of this software and associated documentation files (the "Software"), | |
6 | + * to deal in the Software without restriction, including without limitation | |
7 | + * the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
8 | + * and/or sell copies of the Software, and to permit persons to whom the | |
9 | + * Software is furnished to do so, subject to the following conditions: | |
10 | + * | |
11 | + * The above copyright notice and this permission notice shall be included in | |
12 | + * all copies or substantial portions of the Software. | |
13 | + * | |
14 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
15 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
16 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
17 | + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
18 | + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |
19 | + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | |
20 | + * DEALINGS IN THE SOFTWARE. | |
21 | + * | |
22 | + * The Software uses a third-party library (WiiRemoteJ) which is not part of | |
23 | + * the Software and is subject to its own license. | |
24 | + */ | |
25 | + | |
26 | +package org.uweschmidt.wiimote.whiteboard.gui; | |
27 | + | |
28 | +import java.awt.Point; | |
29 | +import java.awt.SystemColor; | |
30 | +import java.awt.event.KeyAdapter; | |
31 | +import java.awt.event.KeyEvent; | |
32 | +import java.util.Map; | |
33 | +import java.util.Timer; | |
34 | +import java.util.TimerTask; | |
35 | + | |
36 | +import javax.swing.BorderFactory; | |
37 | +import javax.swing.JDialog; | |
38 | +import javax.swing.JPanel; | |
39 | + | |
40 | +import net.miginfocom.swing.MigLayout; | |
41 | + | |
42 | +import org.jdesktop.application.Action; | |
43 | +import org.jdesktop.application.Application; | |
44 | +import org.uweschmidt.wiimote.whiteboard.WiimoteDataHandler; | |
45 | +import org.uweschmidt.wiimote.whiteboard.WiimoteWhiteboard; | |
46 | +import org.uweschmidt.wiimote.whiteboard.WiimoteDataHandler.WiimoteDataListener; | |
47 | +import org.uweschmidt.wiimote.whiteboard.ds.IRDot; | |
48 | +import org.uweschmidt.wiimote.whiteboard.ds.Wiimote; | |
49 | +import org.uweschmidt.wiimote.whiteboard.preferences.WWPreferences; | |
50 | +import org.uweschmidt.wiimote.whiteboard.util.Util; | |
51 | + | |
52 | +@SuppressWarnings("serial") | |
53 | +public class CameraMonitor extends JDialog implements WiimoteDataListener { | |
54 | + | |
55 | + private static final long REPAINT_FREQ = 1000 / 25; | |
56 | + | |
57 | + private JPanel canvas; | |
58 | + private IRDot[][] lights = new IRDot[WWPreferences.WIIMOTES+1][4]; | |
59 | + private LightLabel[][] labels = new LightLabel[WWPreferences.WIIMOTES+1][4]; | |
60 | + | |
61 | + public CameraMonitor(WiimoteDataHandler dh) { | |
62 | + super(Application.getInstance(WiimoteWhiteboard.class).getMainFrame(), Util.getResourceMap(CameraMonitor.class).getString("monitor.Action.text")); | |
63 | + getRootPane().putClientProperty("Window.style", "small"); | |
64 | + setLayout(new MigLayout()); | |
65 | + | |
66 | + dh.addWiimoteDataListener(this); | |
67 | + | |
68 | + canvas = new JPanel(null, true); | |
69 | + canvas.setOpaque(true); | |
70 | + canvas.setBorder(BorderFactory.createLineBorder(SystemColor.inactiveCaptionBorder)); | |
71 | + add(canvas, "w 50sp, h 50sp, grow, push"); | |
72 | + | |
73 | + addKeyListener(new KeyAdapter() { | |
74 | + @Override | |
75 | + public void keyPressed(KeyEvent e) { | |
76 | + if (e.getKeyCode() == KeyEvent.VK_ESCAPE) { | |
77 | + setVisible(false); | |
78 | + } | |
79 | + } | |
80 | + }); | |
81 | + | |
82 | + pack(); | |
83 | + setLocationRelativeTo(null); | |
84 | + | |
85 | + new Timer(true).schedule(new UpdateTask(), 0, REPAINT_FREQ); | |
86 | + } | |
87 | + | |
88 | + @Action | |
89 | + public void monitor() { | |
90 | + setVisible(true); | |
91 | + } | |
92 | + | |
93 | + private class UpdateTask extends TimerTask { | |
94 | + @Override | |
95 | + public void run() { | |
96 | + if (isVisible()) { | |
97 | + for (int i = 1; i <= WWPreferences.WIIMOTES; i++) | |
98 | + for (int j = 0; j < 4; j++) { | |
99 | + if (labels[i][j] != null) | |
100 | + labels[i][j].update(); | |
101 | + } | |
102 | + } | |
103 | + } | |
104 | + } | |
105 | + | |
106 | + public void irLights(Wiimote wiimote, IRDot[] lights) { | |
107 | + this.lights[wiimote.getId()] = lights; | |
108 | + } | |
109 | + | |
110 | + public void irWarped(Map<Wiimote, IRDot[]> data, Point[] warped) { | |
111 | + } | |
112 | + | |
113 | + public void batteryLevel(Wiimote wiimote, double level) { | |
114 | + } | |
115 | + | |
116 | + public void wiimoteConnected(Wiimote wiimote) { | |
117 | + for (int i = 0; i < 4; i++) | |
118 | + canvas.add(labels[wiimote.getId()][i] = new LightLabel(canvas, lights, wiimote.getId(), i + 1)); | |
119 | + } | |
120 | + | |
121 | + public void wiimoteDisconnected(Wiimote wiimote) { | |
122 | + for (int i = 0; i < 4; i++) { | |
123 | + canvas.remove(labels[wiimote.getId()][i]); | |
124 | + labels[wiimote.getId()][i] = null; | |
125 | + } | |
126 | + } | |
127 | + | |
128 | +} | ... | ... |
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/gui/HelpBook.java
0 → 100644
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/gui/HelpBook.java~
0 → 100644
... | ... | @@ -0,0 +1,34 @@ |
1 | +/* | |
2 | + * Copyright (C) 2008-2009, Uwe Schmidt | |
3 | + * | |
4 | + * Permission is hereby granted, free of charge, to any person obtaining a | |
5 | + * copy of this software and associated documentation files (the "Software"), | |
6 | + * to deal in the Software without restriction, including without limitation | |
7 | + * the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
8 | + * and/or sell copies of the Software, and to permit persons to whom the | |
9 | + * Software is furnished to do so, subject to the following conditions: | |
10 | + * | |
11 | + * The above copyright notice and this permission notice shall be included in | |
12 | + * all copies or substantial portions of the Software. | |
13 | + * | |
14 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
15 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
16 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
17 | + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
18 | + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |
19 | + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | |
20 | + * DEALINGS IN THE SOFTWARE. | |
21 | + * | |
22 | + * The Software uses a third-party library (WiiRemoteJ) which is not part of | |
23 | + * the Software and is subject to its own license. | |
24 | + */ | |
25 | + | |
26 | +package org.uweschmidt.wiimote.whiteboard.gui; | |
27 | + | |
28 | +public class HelpBook { | |
29 | + static { | |
30 | + System.loadLibrary("HelpBookJNI"); | |
31 | + } | |
32 | + | |
33 | + public static native void launchHelpViewer(); | |
34 | +} | ... | ... |
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/gui/HelpHandler.java
0 → 100644
... | ... | @@ -0,0 +1,24 @@ |
1 | +package org.mote.wiimote.whiteboard.gui; | |
2 | + | |
3 | +import javax.swing.JOptionPane; | |
4 | + | |
5 | +import org.jdesktop.application.Action; | |
6 | +import org.mote.wiimote.whiteboard.WiimoteWhiteboard; | |
7 | +import org.mote.wiimote.whiteboard.util.BareBonesBrowserLaunch; | |
8 | +import org.mote.wiimote.whiteboard.util.Util; | |
9 | + | |
10 | +@SuppressWarnings("serial") | |
11 | +public class HelpHandler { | |
12 | + | |
13 | + @Action | |
14 | + public void help() { | |
15 | + if (Util.INSIDE_APP_BUNDLE) { | |
16 | + HelpBook.launchHelpViewer(); | |
17 | + } else { | |
18 | + if (JOptionPane.showConfirmDialog(null, Util.getResourceMap(HelpHandler.class).getString("helpQuestion"), Util.getResourceMap(HelpHandler.class).getString("help.Action.text"), JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) { | |
19 | + BareBonesBrowserLaunch.openURL(WiimoteWhiteboard.getProperty("onlineHelp")); | |
20 | + } | |
21 | + } | |
22 | + } | |
23 | + | |
24 | +} | ... | ... |
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/gui/HelpHandler.java~
0 → 100644
... | ... | @@ -0,0 +1,49 @@ |
1 | +/* | |
2 | + * Copyright (C) 2008-2009, Uwe Schmidt | |
3 | + * | |
4 | + * Permission is hereby granted, free of charge, to any person obtaining a | |
5 | + * copy of this software and associated documentation files (the "Software"), | |
6 | + * to deal in the Software without restriction, including without limitation | |
7 | + * the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
8 | + * and/or sell copies of the Software, and to permit persons to whom the | |
9 | + * Software is furnished to do so, subject to the following conditions: | |
10 | + * | |
11 | + * The above copyright notice and this permission notice shall be included in | |
12 | + * all copies or substantial portions of the Software. | |
13 | + * | |
14 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
15 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
16 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
17 | + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
18 | + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |
19 | + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | |
20 | + * DEALINGS IN THE SOFTWARE. | |
21 | + * | |
22 | + * The Software uses a third-party library (WiiRemoteJ) which is not part of | |
23 | + * the Software and is subject to its own license. | |
24 | + */ | |
25 | + | |
26 | +package org.uweschmidt.wiimote.whiteboard.gui; | |
27 | + | |
28 | +import javax.swing.JOptionPane; | |
29 | + | |
30 | +import org.jdesktop.application.Action; | |
31 | +import org.uweschmidt.wiimote.whiteboard.WiimoteWhiteboard; | |
32 | +import org.uweschmidt.wiimote.whiteboard.util.BareBonesBrowserLaunch; | |
33 | +import org.uweschmidt.wiimote.whiteboard.util.Util; | |
34 | + | |
35 | +@SuppressWarnings("serial") | |
36 | +public class HelpHandler { | |
37 | + | |
38 | + @Action | |
39 | + public void help() { | |
40 | + if (Util.INSIDE_APP_BUNDLE) { | |
41 | + HelpBook.launchHelpViewer(); | |
42 | + } else { | |
43 | + if (JOptionPane.showConfirmDialog(null, Util.getResourceMap(HelpHandler.class).getString("helpQuestion"), Util.getResourceMap(HelpHandler.class).getString("help.Action.text"), JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) { | |
44 | + BareBonesBrowserLaunch.openURL(WiimoteWhiteboard.getProperty("onlineHelp")); | |
45 | + } | |
46 | + } | |
47 | + } | |
48 | + | |
49 | +} | ... | ... |
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/gui/LightLabel.java
0 → 100644
... | ... | @@ -0,0 +1,70 @@ |
1 | +package org.mote.wiimote.whiteboard.gui; | |
2 | + | |
3 | +import java.awt.Color; | |
4 | +import java.awt.Graphics; | |
5 | +import java.awt.Graphics2D; | |
6 | +import java.awt.RenderingHints; | |
7 | +import java.awt.geom.Point2D; | |
8 | + | |
9 | +import javax.swing.JLabel; | |
10 | +import javax.swing.JPanel; | |
11 | +import javax.swing.SwingConstants; | |
12 | + | |
13 | +import org.uweschmidt.wiimote.whiteboard.ds.IRDot; | |
14 | + | |
15 | +@SuppressWarnings("serial") | |
16 | +public class LightLabel extends JLabel { | |
17 | + | |
18 | + private static final int RADIUS = 10; | |
19 | + | |
20 | + private final int id, number; | |
21 | + private Point2D[][] lights; | |
22 | + private JPanel canvas; | |
23 | + | |
24 | + public LightLabel(JPanel canvas, Point2D[][] lights, int id, int number) { | |
25 | + this(canvas, lights, id, number, WiimoteIcon.COLORS[id - 1]); | |
26 | + } | |
27 | + | |
28 | + public LightLabel(JPanel canvas, Point2D[][] lights, int id, int number, Color bg) { | |
29 | + super(String.valueOf(number)); | |
30 | + this.id = id; | |
31 | + this.number = number; | |
32 | + this.lights = lights; | |
33 | + this.canvas = canvas; | |
34 | + setHorizontalAlignment(SwingConstants.CENTER); | |
35 | + setForeground(Color.black); | |
36 | + setBackground(bg); | |
37 | + } | |
38 | + | |
39 | + public void update() { | |
40 | + Point2D l = lights[id][number - 1]; | |
41 | + if (l != null) { | |
42 | + int x = (int) Math.round(l.getX() * canvas.getWidth()); | |
43 | + int y = canvas.getHeight() - (int) Math.round(l.getY() * canvas.getHeight()); | |
44 | + this.setBounds(x - RADIUS, y - RADIUS, RADIUS * 2, RADIUS * 2); | |
45 | + setVisible(true); | |
46 | + } else { | |
47 | + setVisible(false); | |
48 | + } | |
49 | + } | |
50 | + | |
51 | + @Override | |
52 | + protected void paintComponent(Graphics g) { | |
53 | + Graphics2D g2d = (Graphics2D) g; | |
54 | + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); | |
55 | + if (lights[id][number - 1] != null) { | |
56 | + g2d.setColor(this.getBackground()); | |
57 | + g2d.fillOval(1, 1, RADIUS * 2 - 2, RADIUS * 2 - 2); | |
58 | + g2d.setColor(this.getForeground()); | |
59 | + g2d.drawOval(1, 1, RADIUS * 2 - 2, RADIUS * 2 - 2); | |
60 | + | |
61 | + double scale = lights[id][number - 1] instanceof IRDot ? ((IRDot)lights[id][number - 1]).getSize() : -1; | |
62 | + if (scale != -1) { | |
63 | + g2d.setColor(Color.cyan); | |
64 | + final int d = (int)Math.round(RADIUS * scale * 10); | |
65 | + g2d.fillOval(RADIUS - d/2, RADIUS - d/2, d, d); | |
66 | + } | |
67 | + } | |
68 | + super.paintComponent(g); | |
69 | + } | |
70 | +} | ... | ... |
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/gui/LightLabel.java~
0 → 100644
... | ... | @@ -0,0 +1,98 @@ |
1 | +/* | |
2 | + * Copyright (C) 2008-2009, Uwe Schmidt | |
3 | + * | |
4 | + * Permission is hereby granted, free of charge, to any person obtaining a | |
5 | + * copy of this software and associated documentation files (the "Software"), | |
6 | + * to deal in the Software without restriction, including without limitation | |
7 | + * the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
8 | + * and/or sell copies of the Software, and to permit persons to whom the | |
9 | + * Software is furnished to do so, subject to the following conditions: | |
10 | + * | |
11 | + * The above copyright notice and this permission notice shall be included in | |
12 | + * all copies or substantial portions of the Software. | |
13 | + * | |
14 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
15 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
16 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
17 | + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
18 | + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |
19 | + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | |
20 | + * DEALINGS IN THE SOFTWARE. | |
21 | + * | |
22 | + * The Software uses a third-party library (WiiRemoteJ) which is not part of | |
23 | + * the Software and is subject to its own license. | |
24 | + */ | |
25 | + | |
26 | +package org.uweschmidt.wiimote.whiteboard.gui; | |
27 | + | |
28 | +import java.awt.Color; | |
29 | +import java.awt.Graphics; | |
30 | +import java.awt.Graphics2D; | |
31 | +import java.awt.RenderingHints; | |
32 | +import java.awt.geom.Point2D; | |
33 | + | |
34 | +import javax.swing.JLabel; | |
35 | +import javax.swing.JPanel; | |
36 | +import javax.swing.SwingConstants; | |
37 | + | |
38 | +import org.uweschmidt.wiimote.whiteboard.ds.IRDot; | |
39 | + | |
40 | +@SuppressWarnings("serial") | |
41 | +public class LightLabel extends JLabel { | |
42 | + | |
43 | + private static final int RADIUS = 10; | |
44 | + | |
45 | + private final int id, number; | |
46 | + private Point2D[][] lights; | |
47 | + private JPanel canvas; | |
48 | + | |
49 | + public LightLabel(JPanel canvas, Point2D[][] lights, int id, int number) { | |
50 | + this(canvas, lights, id, number, WiimoteIcon.COLORS[id - 1]); | |
51 | + } | |
52 | + | |
53 | + public LightLabel(JPanel canvas, Point2D[][] lights, int id, int number, Color bg) { | |
54 | + super(String.valueOf(number)); | |
55 | + this.id = id; | |
56 | + this.number = number; | |
57 | + this.lights = lights; | |
58 | + this.canvas = canvas; | |
59 | + // setOpaque(true); | |
60 | + setHorizontalAlignment(SwingConstants.CENTER); | |
61 | + // setBorder(BorderFactory.createLineBorder(Color.black)); | |
62 | + setForeground(Color.black); | |
63 | + setBackground(bg); | |
64 | + } | |
65 | + | |
66 | + public void update() { | |
67 | + Point2D l = lights[id][number - 1]; | |
68 | + if (l != null) { | |
69 | + int x = (int) Math.round(l.getX() * canvas.getWidth()); | |
70 | + int y = canvas.getHeight() - (int) Math.round(l.getY() * canvas.getHeight()); | |
71 | + this.setBounds(x - RADIUS, y - RADIUS, RADIUS * 2, RADIUS * 2); | |
72 | + setVisible(true); | |
73 | + } else { | |
74 | + setVisible(false); | |
75 | + } | |
76 | + } | |
77 | + | |
78 | + @Override | |
79 | + protected void paintComponent(Graphics g) { | |
80 | + Graphics2D g2d = (Graphics2D) g; | |
81 | + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); | |
82 | + if (lights[id][number - 1] != null) { | |
83 | + g2d.setColor(this.getBackground()); | |
84 | + g2d.fillOval(1, 1, RADIUS * 2 - 2, RADIUS * 2 - 2); | |
85 | + g2d.setColor(this.getForeground()); | |
86 | + g2d.drawOval(1, 1, RADIUS * 2 - 2, RADIUS * 2 - 2); | |
87 | + | |
88 | + // XXX explain cyan oval for size | |
89 | + double scale = lights[id][number - 1] instanceof IRDot ? ((IRDot)lights[id][number - 1]).getSize() : -1; | |
90 | + if (scale != -1) { | |
91 | + g2d.setColor(Color.cyan); | |
92 | + final int d = (int)Math.round(RADIUS * scale * 10); | |
93 | + g2d.fillOval(RADIUS - d/2, RADIUS - d/2, d, d); | |
94 | + } | |
95 | + } | |
96 | + super.paintComponent(g); | |
97 | + } | |
98 | +} | |
0 | 99 | \ No newline at end of file | ... | ... |
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/gui/LogWindow.java
0 → 100644
... | ... | @@ -0,0 +1,134 @@ |
1 | +package org.mote.wiimote.whiteboard.gui; | |
2 | + | |
3 | +import java.awt.BorderLayout; | |
4 | +import java.awt.event.KeyAdapter; | |
5 | +import java.awt.event.KeyEvent; | |
6 | +import java.io.PrintWriter; | |
7 | +import java.io.StringWriter; | |
8 | +import java.io.Writer; | |
9 | +import java.util.Calendar; | |
10 | +import java.util.logging.Handler; | |
11 | +import java.util.logging.Level; | |
12 | +import java.util.logging.LogRecord; | |
13 | +import java.util.logging.Logger; | |
14 | + | |
15 | +import javax.swing.JDialog; | |
16 | +import javax.swing.JLabel; | |
17 | +import javax.swing.JScrollPane; | |
18 | +import javax.swing.JTextArea; | |
19 | + | |
20 | +import net.miginfocom.swing.MigLayout; | |
21 | + | |
22 | +import org.jdesktop.application.Action; | |
23 | +import org.jdesktop.application.Application; | |
24 | +import org.mote.wiimote.whiteboard.WiimoteWhiteboard; | |
25 | +import org.mote.wiimote.whiteboard.preferences.WWPreferences; | |
26 | +import org.mote.wiimote.whiteboard.util.Util; | |
27 | + | |
28 | +@SuppressWarnings("serial") | |
29 | +public class LogWindow extends JDialog { | |
30 | + | |
31 | + private JTextArea log; | |
32 | + private JDialog failedDialog; | |
33 | + | |
34 | + public LogWindow() { | |
35 | + super(Application.getInstance(WiimoteWhiteboard.class).getMainFrame(), WiimoteWhiteboard.getProperty("id") + " " + Util.getResourceMap(LogWindow.class).getString("log.Action.text")); | |
36 | + getRootPane().putClientProperty("Window.style", "small"); | |
37 | + setLayout(new MigLayout()); | |
38 | + | |
39 | + log = Util.newComponent(JTextArea.class, "log"); | |
40 | + add(new JScrollPane(log), "w 66sp, h 33sp, grow, push"); | |
41 | + | |
42 | + Logger.getLogger("wiiremotej").addHandler(new LogHandler("WiiRemoteJ: ")); | |
43 | + WiimoteWhiteboard.getLogger().addHandler(new LogHandler("")); | |
44 | + | |
45 | + log.addKeyListener(new KeyAdapter() { | |
46 | + @Override | |
47 | + public void keyPressed(KeyEvent e) { | |
48 | + if (e.getKeyCode() == KeyEvent.VK_ESCAPE) { | |
49 | + setVisible(false); | |
50 | + } | |
51 | + } | |
52 | + }); | |
53 | + | |
54 | + Util.getResourceMap(LogWindow.class).injectComponents(this); | |
55 | + pack(); | |
56 | + Util.placeDialogWindow(this, getWidth(), getHeight()); | |
57 | + | |
58 | + failedDialog = Util.newComponent(JDialog.class, "failedDialog"); | |
59 | + failedDialog.addKeyListener(new KeyAdapter() { | |
60 | + @Override | |
61 | + public void keyPressed(KeyEvent e) { | |
62 | + if (e.getKeyCode() == KeyEvent.VK_ESCAPE) { | |
63 | + failedDialog.setVisible(false); | |
64 | + } | |
65 | + } | |
66 | + }); | |
67 | + failedDialog.setAlwaysOnTop(true); | |
68 | + failedDialog.getRootPane().putClientProperty("Window.style", "small"); | |
69 | + failedDialog.getRootPane().putClientProperty("Window.alpha", new Float(0.90)); | |
70 | + failedDialog.setLayout(new BorderLayout()); | |
71 | + failedDialog.add(Util.newComponent(JLabel.class, "failedLabel"), BorderLayout.CENTER); | |
72 | + Util.getResourceMap(LogWindow.class).injectComponents(failedDialog); | |
73 | + failedDialog.pack(); | |
74 | + failedDialog.setBounds(getX()+getWidth()/2-(failedDialog.getWidth()+20)/2, getY()+getHeight()+10, failedDialog.getWidth()+20, failedDialog.getHeight()+20); | |
75 | + } | |
76 | + | |
77 | + @Action | |
78 | + public void log() { | |
79 | + setVisible(!isVisible()); | |
80 | + } | |
81 | + | |
82 | + private class LogHandler extends Handler { | |
83 | + private static final String DIRECT_CONNECTION_TIMEOUT = "Failed to open baseband connection"; | |
84 | + private static final String CONNECTION_FAILED = "WiiRemote failed to connect!"; | |
85 | + private static final String CONNECTION_SUCCEEDED = "Initial connection complete."; | |
86 | + private final String name; | |
87 | + public LogHandler(String name) { | |
88 | + this.name = name; | |
89 | + setLevel(Level.ALL); | |
90 | + } | |
91 | + @Override | |
92 | + public void close() throws SecurityException {} | |
93 | + @Override | |
94 | + public void flush() {} | |
95 | + | |
96 | + @Override | |
97 | + public void publish(LogRecord record) { | |
98 | + Level level = record.getLevel(); | |
99 | + String msg = record.getMessage(); | |
100 | + Throwable e = record.getThrown(); | |
101 | + boolean failed = false; | |
102 | + | |
103 | + if (!WWPreferences.WIIMOTE_BT_ADDRESSES.isEmpty() && e != null && e.getCause() != null && e.getCause().getMessage().startsWith(DIRECT_CONNECTION_TIMEOUT)) | |
104 | + return; | |
105 | + | |
106 | + log.append(String.format("%7s %tT %s%s\n", level.getName(), Calendar.getInstance(), name, msg)); | |
107 | + if (e != null) { | |
108 | + log.append(getStackTrace(e)); | |
109 | + failed = CONNECTION_FAILED.equals(e.getMessage()); | |
110 | + } | |
111 | + log.setCaretPosition(log.getText().length()-1); | |
112 | + | |
113 | + if (!failed && level == Level.SEVERE) { | |
114 | + if (!LogWindow.this.isVisible()) { | |
115 | + LogWindow.this.setVisible(true); | |
116 | + } | |
117 | + } | |
118 | + | |
119 | + if (CONNECTION_SUCCEEDED.equals(msg)) { | |
120 | + failedDialog.setVisible(false); | |
121 | + } else if (failed) { | |
122 | + failedDialog.setVisible(true); | |
123 | + } | |
124 | + } | |
125 | + } | |
126 | + | |
127 | + private static String getStackTrace(Throwable e) { | |
128 | + final Writer result = new StringWriter(); | |
129 | + final PrintWriter printWriter = new PrintWriter(result); | |
130 | + e.printStackTrace(printWriter); | |
131 | + return result.toString(); | |
132 | + } | |
133 | + | |
134 | +} | ... | ... |
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/gui/LogWindow.java~
0 → 100644
... | ... | @@ -0,0 +1,171 @@ |
1 | +/* | |
2 | + * Copyright (C) 2008-2009, Uwe Schmidt | |
3 | + * | |
4 | + * Permission is hereby granted, free of charge, to any person obtaining a | |
5 | + * copy of this software and associated documentation files (the "Software"), | |
6 | + * to deal in the Software without restriction, including without limitation | |
7 | + * the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
8 | + * and/or sell copies of the Software, and to permit persons to whom the | |
9 | + * Software is furnished to do so, subject to the following conditions: | |
10 | + * | |
11 | + * The above copyright notice and this permission notice shall be included in | |
12 | + * all copies or substantial portions of the Software. | |
13 | + * | |
14 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
15 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
16 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
17 | + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
18 | + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |
19 | + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | |
20 | + * DEALINGS IN THE SOFTWARE. | |
21 | + * | |
22 | + * The Software uses a third-party library (WiiRemoteJ) which is not part of | |
23 | + * the Software and is subject to its own license. | |
24 | + */ | |
25 | + | |
26 | +package org.uweschmidt.wiimote.whiteboard.gui; | |
27 | + | |
28 | +import java.awt.BorderLayout; | |
29 | +import java.awt.event.KeyAdapter; | |
30 | +import java.awt.event.KeyEvent; | |
31 | +import java.io.PrintWriter; | |
32 | +import java.io.StringWriter; | |
33 | +import java.io.Writer; | |
34 | +import java.util.Calendar; | |
35 | +import java.util.logging.Handler; | |
36 | +import java.util.logging.Level; | |
37 | +import java.util.logging.LogRecord; | |
38 | +import java.util.logging.Logger; | |
39 | + | |
40 | +import javax.swing.JDialog; | |
41 | +import javax.swing.JLabel; | |
42 | +import javax.swing.JScrollPane; | |
43 | +import javax.swing.JTextArea; | |
44 | + | |
45 | +import net.miginfocom.swing.MigLayout; | |
46 | + | |
47 | +import org.jdesktop.application.Action; | |
48 | +import org.jdesktop.application.Application; | |
49 | +import org.uweschmidt.wiimote.whiteboard.WiimoteWhiteboard; | |
50 | +import org.uweschmidt.wiimote.whiteboard.preferences.WWPreferences; | |
51 | +import org.uweschmidt.wiimote.whiteboard.util.Util; | |
52 | + | |
53 | +@SuppressWarnings("serial") | |
54 | +public class LogWindow extends JDialog { | |
55 | + | |
56 | + private JTextArea log; | |
57 | + private JDialog failedDialog; | |
58 | + | |
59 | + public LogWindow() { | |
60 | + super(Application.getInstance(WiimoteWhiteboard.class).getMainFrame(), WiimoteWhiteboard.getProperty("id") + " " + Util.getResourceMap(LogWindow.class).getString("log.Action.text")); | |
61 | + getRootPane().putClientProperty("Window.style", "small"); | |
62 | +// setLayout(new BorderLayout()); | |
63 | + setLayout(new MigLayout()); | |
64 | +// if (getContentPane() instanceof JPanel) { | |
65 | +// ((JPanel)getContentPane()).setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); | |
66 | +// } | |
67 | + | |
68 | + log = Util.newComponent(JTextArea.class, "log"); | |
69 | +// add(new JScrollPane(log), BorderLayout.CENTER); | |
70 | + add(new JScrollPane(log), "w 66sp, h 33sp, grow, push"); | |
71 | + | |
72 | + Logger.getLogger("wiiremotej").addHandler(new LogHandler("WiiRemoteJ: ")); | |
73 | + WiimoteWhiteboard.getLogger().addHandler(new LogHandler("")); | |
74 | + | |
75 | + log.addKeyListener(new KeyAdapter() { | |
76 | + @Override | |
77 | + public void keyPressed(KeyEvent e) { | |
78 | + if (e.getKeyCode() == KeyEvent.VK_ESCAPE) { | |
79 | + setVisible(false); | |
80 | + } | |
81 | + } | |
82 | + }); | |
83 | + | |
84 | + Util.getResourceMap(LogWindow.class).injectComponents(this); | |
85 | + pack(); | |
86 | +// Util.placeDialogWindow(this, 500, 300); | |
87 | + Util.placeDialogWindow(this, getWidth(), getHeight()); | |
88 | + | |
89 | + failedDialog = Util.newComponent(JDialog.class, "failedDialog"); | |
90 | + failedDialog.addKeyListener(new KeyAdapter() { | |
91 | + @Override | |
92 | + public void keyPressed(KeyEvent e) { | |
93 | + if (e.getKeyCode() == KeyEvent.VK_ESCAPE) { | |
94 | + failedDialog.setVisible(false); | |
95 | + } | |
96 | + } | |
97 | + }); | |
98 | + failedDialog.setAlwaysOnTop(true); | |
99 | + failedDialog.getRootPane().putClientProperty("Window.style", "small"); | |
100 | + failedDialog.getRootPane().putClientProperty("Window.alpha", new Float(0.90)); | |
101 | + failedDialog.setLayout(new BorderLayout()); | |
102 | + failedDialog.add(Util.newComponent(JLabel.class, "failedLabel"), BorderLayout.CENTER); | |
103 | + Util.getResourceMap(LogWindow.class).injectComponents(failedDialog); | |
104 | + failedDialog.pack(); | |
105 | +// failedDialog.setBounds(getX(), getY()+getHeight()+10, getWidth(), failedDialog.getHeight()+20); | |
106 | + failedDialog.setBounds(getX()+getWidth()/2-(failedDialog.getWidth()+20)/2, getY()+getHeight()+10, failedDialog.getWidth()+20, failedDialog.getHeight()+20); | |
107 | +// failedDialog.setVisible(true); | |
108 | + } | |
109 | + | |
110 | + @Action | |
111 | + public void log() { | |
112 | + setVisible(!isVisible()); | |
113 | + } | |
114 | + | |
115 | + private class LogHandler extends Handler { | |
116 | + private static final String DIRECT_CONNECTION_TIMEOUT = "Failed to open baseband connection"; | |
117 | + private static final String CONNECTION_FAILED = "WiiRemote failed to connect!"; | |
118 | + private static final String CONNECTION_SUCCEEDED = "Initial connection complete."; | |
119 | + private final String name; | |
120 | + public LogHandler(String name) { | |
121 | + this.name = name; | |
122 | + setLevel(Level.ALL); | |
123 | + } | |
124 | + @Override | |
125 | + public void close() throws SecurityException {} | |
126 | + @Override | |
127 | + public void flush() {} | |
128 | + | |
129 | + @Override | |
130 | + public void publish(LogRecord record) { | |
131 | + Level level = record.getLevel(); | |
132 | + String msg = record.getMessage(); | |
133 | + Throwable e = record.getThrown(); | |
134 | + boolean failed = false; | |
135 | + | |
136 | + // skip that error (apparently timeout on direct connection attempts) | |
137 | + if (!WWPreferences.WIIMOTE_BT_ADDRESSES.isEmpty() && e != null && e.getCause() != null && e.getCause().getMessage().startsWith(DIRECT_CONNECTION_TIMEOUT)) | |
138 | + return; | |
139 | + | |
140 | + log.append(String.format("%7s %tT %s%s\n", level.getName(), Calendar.getInstance(), name, msg)); | |
141 | + if (e != null) { | |
142 | + log.append(getStackTrace(e)); | |
143 | + failed = CONNECTION_FAILED.equals(e.getMessage()); | |
144 | + } | |
145 | + log.setCaretPosition(log.getText().length()-1); | |
146 | + | |
147 | + if (!failed && level == Level.SEVERE) { | |
148 | + if (!LogWindow.this.isVisible()) { | |
149 | +// LogWindow.this.pack(); | |
150 | +// Util.placeDialogWindow(LogWindow.this, LogWindow.this.getWidth(), LogWindow.this.getHeight()); | |
151 | + LogWindow.this.setVisible(true); | |
152 | + } | |
153 | + } | |
154 | + | |
155 | + if (CONNECTION_SUCCEEDED.equals(msg)) { | |
156 | +// LogWindow.this.setVisible(false); | |
157 | + failedDialog.setVisible(false); | |
158 | + } else if (failed) { | |
159 | + failedDialog.setVisible(true); | |
160 | + } | |
161 | + } | |
162 | + } | |
163 | + | |
164 | + private static String getStackTrace(Throwable e) { | |
165 | + final Writer result = new StringWriter(); | |
166 | + final PrintWriter printWriter = new PrintWriter(result); | |
167 | + e.printStackTrace(printWriter); | |
168 | + return result.toString(); | |
169 | + } | |
170 | + | |
171 | +} | ... | ... |
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/gui/MainPanel.java
0 → 100644
... | ... | @@ -0,0 +1,332 @@ |
1 | +package org.mote.wiimote.whiteboard.gui; | |
2 | + | |
3 | +import java.awt.Color; | |
4 | +import java.awt.Font; | |
5 | +import java.awt.Graphics; | |
6 | +import java.awt.Point; | |
7 | +import java.awt.SystemColor; | |
8 | +import java.awt.event.ActionEvent; | |
9 | +import java.awt.event.ActionListener; | |
10 | +import java.awt.event.WindowEvent; | |
11 | +import java.awt.event.WindowFocusListener; | |
12 | +import java.util.Map; | |
13 | +import java.util.logging.Level; | |
14 | + | |
15 | +import javax.swing.BorderFactory; | |
16 | +import javax.swing.ButtonGroup; | |
17 | +import javax.swing.ImageIcon; | |
18 | +import javax.swing.JButton; | |
19 | +import javax.swing.JCheckBox; | |
20 | +import javax.swing.JLabel; | |
21 | +import javax.swing.JOptionPane; | |
22 | +import javax.swing.JPanel; | |
23 | +import javax.swing.JProgressBar; | |
24 | +import javax.swing.JRadioButton; | |
25 | +import javax.swing.JSeparator; | |
26 | +import javax.swing.SwingConstants; | |
27 | + | |
28 | +import net.miginfocom.swing.MigLayout; | |
29 | + | |
30 | +import org.jdesktop.application.Action; | |
31 | +import org.jdesktop.application.Application; | |
32 | +import org.jdesktop.application.ResourceMap; | |
33 | +import org.mote.wiimote.whiteboard.WiimoteDataHandler; | |
34 | +import org.mote.wiimote.whiteboard.WiimoteWhiteboard; | |
35 | +import org.mote.wiimote.whiteboard.WiimoteDataHandler.WiimoteDataListener; | |
36 | +import org.mote.wiimote.whiteboard.calibration.WiimoteCalibration; | |
37 | +import org.mote.wiimote.whiteboard.calibration.WiimoteCalibration.CalibrationEvent; | |
38 | +import org.mote.wiimote.whiteboard.calibration.WiimoteCalibration.CalibrationEventListener; | |
39 | +import org.mote.wiimote.whiteboard.ds.IRDot; | |
40 | +import org.mote.wiimote.whiteboard.ds.Wiimote; | |
41 | +import org.mote.wiimote.whiteboard.preferences.WWPreferences; | |
42 | +import org.mote.wiimote.whiteboard.util.Util; | |
43 | + | |
44 | +@SuppressWarnings("serial") | |
45 | +public class MainPanel extends JPanel implements WiimoteDataListener, CalibrationEventListener { | |
46 | + | |
47 | + private static final ImageIcon CALIBRATED = new ImageIcon(MainPanel.class.getResource("resources/icons/ok.png")); | |
48 | + private static final ImageIcon NOT_CALIBRATED = new ImageIcon(MainPanel.class.getResource("resources/icons/warning.png")); | |
49 | + | |
50 | + private static final WWPreferences prefs = WWPreferences.getPreferences(); | |
51 | + | |
52 | + private DotLabel[] dotLabel = new DotLabel[4]; | |
53 | + private JProgressBar[] batteryLevel = new JProgressBar[WWPreferences.WIIMOTES]; | |
54 | + private JButton[] resetButton = new JButton[WWPreferences.WIIMOTES]; | |
55 | + private WiimoteIcon[] wiimoteIcon = new WiimoteIcon[WWPreferences.WIIMOTES]; | |
56 | + private JLabel[] statusLabel = new JLabel[WWPreferences.WIIMOTES]; | |
57 | + private JCheckBox cursorControl; | |
58 | + private JRadioButton moveMouse, leftClick; | |
59 | + private JButton calibrationButton, cameraButton | |
60 | + private ScreenSelector screenSelector; | |
61 | + | |
62 | + private boolean notifiedLowBattery = false; | |
63 | + | |
64 | + private Wiimote[] wiimotes = new Wiimote[WWPreferences.WIIMOTES]; | |
65 | + | |
66 | + private WiimoteDataHandler dh; | |
67 | + private WiimoteCalibration calibration; | |
68 | + private ResourceMap r = Util.getResourceMap(MainPanel.class); | |
69 | + | |
70 | + public MainPanel(WiimoteDataHandler dh, WiimoteCalibration calibration) { | |
71 | + this.dh = dh; | |
72 | + this.calibration = calibration; | |
73 | + | |
74 | + dh.addWiimoteDataListener(this); | |
75 | + calibration.addCalibrationEventListener(this); | |
76 | + | |
77 | + createComponents(); | |
78 | + | |
79 | + getValues(); | |
80 | + update(); | |
81 | + } | |
82 | + | |
83 | + public void getValues() { | |
84 | + cursorControl.setSelected(dh.isCursorControl()); | |
85 | + leftClick.setSelected(prefs.isLeftClick()); | |
86 | + moveMouse.setSelected(!prefs.isLeftClick()); | |
87 | + } | |
88 | + | |
89 | + public void calibrationEvent(CalibrationEvent e) { | |
90 | + switch (e) { | |
91 | + case FINISHED: | |
92 | + case LOADED: | |
93 | + cursorControl.setSelected(WWPreferences.MOUSE_CONTROL_AFTER_CALIBRATION); | |
94 | + case STARTED: | |
95 | + case SCREEN_CHANGED: | |
96 | + case ABORTED: | |
97 | + update(); | |
98 | + break; | |
99 | + } | |
100 | + } | |
101 | + | |
102 | + public void batteryLevel(Wiimote wiimote, double level) { | |
103 | + batteryLevel[wiimote.getId()-1].setValue((int) Math.round(level * 100)); | |
104 | + batteryLevel[wiimote.getId()-1].setString(r.getString("batteryLevel", level * 100)); | |
105 | + | |
106 | + if (!notifiedLowBattery && prefs.isLowBatteryWarning() && dh.isConnected(wiimote) && level <= .05) { | |
107 | + notifiedLowBattery = true; | |
108 | + WiimoteWhiteboard.getLogger().log(Level.WARNING, r.getString("lowBattery")); | |
109 | + new Thread(new Runnable() { | |
110 | + public void run() { | |
111 | + JOptionPane.showMessageDialog(null, r.getString("lowBattery"), WiimoteWhiteboard.getProperty("id"), JOptionPane.WARNING_MESSAGE); | |
112 | + } | |
113 | + }).start(); | |
114 | + } | |
115 | + } | |
116 | + | |
117 | + public void irLights(Wiimote wiimote, IRDot[] lights) { | |
118 | + for (int i = 0; i < lights.length; i++) { | |
119 | + dotLabel[i].update(wiimote, lights[i] != null); | |
120 | + } | |
121 | + } | |
122 | + | |
123 | + public void irWarped(Map<Wiimote, IRDot[]> data, Point[] warped) { | |
124 | + } | |
125 | + | |
126 | + public void wiimoteConnected(Wiimote wiimote) { | |
127 | + wiimotes[wiimote.getId()-1] = wiimote; | |
128 | + | |
129 | + if (wiimote.getId() > 1) { | |
130 | + for (int i = 0; i < wiimote.getId(); i++) { | |
131 | + wiimoteIcon[i].setVisible(true); | |
132 | + } | |
133 | + } | |
134 | + statusLabel[wiimote.getId()-1].setVisible(true); | |
135 | + | |
136 | + batteryLevel[wiimote.getId()-1].setVisible(true); | |
137 | + resetButton[wiimote.getId()-1].setVisible(true); | |
138 | + cursorControl.setSelected(WWPreferences.MOUSE_CONTROL_AFTER_CALIBRATION); | |
139 | + update(); | |
140 | + } | |
141 | + | |
142 | + public void wiimoteDisconnected(Wiimote wiimote) { | |
143 | + if (wiimote.getId() != 1) batteryLevel[wiimote.getId()-1].setVisible(false); | |
144 | + if (wiimote.getId() != 1) wiimoteIcon[wiimote.getId()-1].setVisible(false); | |
145 | + if (wiimote.getId() != 1) resetButton[wiimote.getId()-1].setVisible(false); | |
146 | + if (wiimote.getId() != 1) statusLabel[wiimote.getId()-1].setVisible(false); | |
147 | + update(); | |
148 | + JOptionPane.showMessageDialog(null, r.getString("disconnected", wiimote.getId()), WiimoteWhiteboard.getProperty("id"), JOptionPane.ERROR_MESSAGE); | |
149 | + Application.getInstance(WiimoteWhiteboard.class).exit(); | |
150 | + } | |
151 | + | |
152 | + | |
153 | + /* | |
154 | + * UPDATE UI WIDGETS | |
155 | + */ | |
156 | + | |
157 | + @Action | |
158 | + public void update() { | |
159 | + calibrationButton.setEnabled(dh.isConnected()); | |
160 | + for (int i = 0; i < WWPreferences.WIIMOTES; i++) { | |
161 | + resetButton[i].setEnabled(dh.isConnected(wiimotes[i])); | |
162 | + wiimoteIcon[i].displayConnected(dh.isConnected(wiimotes[i])); | |
163 | + if (dh.isConnected(wiimotes[i])) { | |
164 | + boolean calibrated = calibration.isCalibrated(wiimotes[i]); | |
165 | + statusLabel[i].setText(r.getString(calibrated ? "calibrated" : "notCalibrated")); | |
166 | + statusLabel[i].setIcon(calibrated ? CALIBRATED : NOT_CALIBRATED); | |
167 | + } | |
168 | + } | |
169 | + cameraButton.setEnabled(dh.isConnected()); | |
170 | + screenSelector.setEnabled(dh.isConnected() && !calibration.inProgress()); | |
171 | + | |
172 | + if (!dh.isConnected()) { | |
173 | + batteryLevel[0].setString(r.getString("searching")); | |
174 | + batteryLevel[0].setValue(0); | |
175 | + batteryLevel[0].setToolTipText(Util.getResourceMap(MainPanel.class).getString("batteryLevelBar.toolTipText")); | |
176 | + } else { | |
177 | + batteryLevel[0].setToolTipText(null); | |
178 | + } | |
179 | + | |
180 | + cursorControl.setEnabled(dh.isConnected() && calibration.isDone() && calibration.isAnyCalibrated(dh.getConnectedWiimotes())); | |
181 | + if (!cursorControl.isEnabled()) | |
182 | + cursorControl.setSelected(false); | |
183 | + | |
184 | + moveMouse.setEnabled(cursorControl.isSelected()); | |
185 | + leftClick.setEnabled(cursorControl.isSelected()); | |
186 | + | |
187 | + dh.setCursorControl(cursorControl.isSelected()); | |
188 | + prefs.setLeftClick(leftClick.isSelected()); | |
189 | + } | |
190 | + | |
191 | + /* | |
192 | + * MAIN PANEL | |
193 | + */ | |
194 | + | |
195 | + private void createComponents() { | |
196 | + setLayout(new MigLayout("nocache, hidemode 3, gap 0", "[fill]")); | |
197 | + | |
198 | + addHeadline(r.getString("wiimoteHeadline"), false); | |
199 | + | |
200 | + for (int i = 0; i < WWPreferences.WIIMOTES; i++) { | |
201 | + add(wiimoteIcon[i] = new WiimoteIcon(i+1), "split"); | |
202 | + if (i > 0) wiimoteIcon[i].setVisible(false); | |
203 | + } | |
204 | + | |
205 | + for (int i = 0; i < WWPreferences.WIIMOTES; i++) { | |
206 | + add(batteryLevel[i] = Util.newComponent(JProgressBar.class, "batteryLevelBar"), (i == 0 ? "newline related/2, " : "") + "split"); | |
207 | + if (i > 0) batteryLevel[i].setVisible(false); | |
208 | + } | |
209 | + | |
210 | + for (int i = 0; i < WWPreferences.WIIMOTES; i++) { | |
211 | + add(statusLabel[i] = Util.newComponent(JLabel.class, "statusLabel"), (i == 0 ? "newline related/2, split, " : "") + "sg, h 16!, center"); | |
212 | + statusLabel[i].setFont(statusLabel[i].getFont().deriveFont(10f)); | |
213 | + if (i > 0) statusLabel[i].setVisible(false); | |
214 | + } | |
215 | + | |
216 | + String gap = Util.MAC_OS_X_LEOPARD_OR_HIGHER ? "0" : "related/2"; | |
217 | + for (int i = 0; i < WWPreferences.WIIMOTES; i++) { | |
218 | + final int j = i; | |
219 | + add(resetButton[i] = Util.newComponent(JButton.class, "resetCameraButton"), (i == 0 ? "newline "+gap+", split, " : "") + ""); | |
220 | + resetButton[i].putClientProperty("JButton.buttonType", "textured"); | |
221 | + resetButton[i].putClientProperty("JComponent.sizeVariant", "small"); | |
222 | + resetButton[i].addActionListener(new ActionListener() { | |
223 | + public void actionPerformed(ActionEvent e) { | |
224 | + try { | |
225 | + dh.enableIR(wiimotes[j]); | |
226 | + } catch (Exception e1) { | |
227 | + e1.printStackTrace(); | |
228 | + } | |
229 | + } | |
230 | + }); | |
231 | + if (i > 0) resetButton[i].setVisible(false); | |
232 | + } | |
233 | + | |
234 | + add(new JSeparator(), String.format("newline %s, span, growx, wrap related", Util.MAC_OS_X_LEOPARD_OR_HIGHER ? "related/3" : "related")); | |
235 | + | |
236 | + add(Util.newComponent(JLabel.class, "visibleDotsLabel"), "split" + (Util.MAC_OS_X ? ", gapleft 4" : "")); | |
237 | + add(dotLabel[0] = new DotLabel("1"), "w 18, h 18"); | |
238 | + add(dotLabel[1] = new DotLabel("2"), "w 18, h 18"); | |
239 | + add(dotLabel[2] = new DotLabel("3"), "w 18, h 18"); | |
240 | + add(dotLabel[3] = new DotLabel("4"), "w 18, h 18, wrap related" + (Util.MAC_OS_X ? ", gapright 4" : "")); | |
241 | + | |
242 | + add(cameraButton = new JButton(Util.getAction(new CameraMonitor(dh), "monitor")), ""); | |
243 | + cameraButton.putClientProperty("JButton.buttonType", "textured"); | |
244 | + | |
245 | + // CALIBRATION | |
246 | + | |
247 | + addHeadline(r.getString("calibrationHeadline")); | |
248 | + | |
249 | + | |
250 | + final JButton infoButton = new JButton(Util.getAction(new CalibrationInfoWindow(calibration, dh), "info")); | |
251 | + infoButton.putClientProperty("JButton.buttonType", "textured"); | |
252 | + infoButton.putClientProperty("JComponent.sizeVariant", "small"); | |
253 | + add(infoButton, "wrap related"); | |
254 | + | |
255 | + | |
256 | + add(screenSelector = new ScreenSelector(calibration, dh), "align center, grow 0, wrap related"); | |
257 | + | |
258 | + add(calibrationButton = Util.newComponent(JButton.class, "calibrationButton"), ""); | |
259 | + calibrationButton.putClientProperty("JButton.buttonType", "textured"); | |
260 | + calibrationButton.addActionListener(new ActionListener() { | |
261 | + public void actionPerformed(ActionEvent e) { | |
262 | + calibration.start(dh.getConnectedWiimotes()); | |
263 | + } | |
264 | + }); | |
265 | + | |
266 | + // MOUSE CONTROL | |
267 | + | |
268 | + addHeadline(r.getString("mouseControlHeadline")); | |
269 | + | |
270 | + add(cursorControl = Util.newComponent(JCheckBox.class, "cursorControl"), "wrap related"); | |
271 | + add(moveMouse = Util.newComponent(JRadioButton.class, "moveMouse"), "pad 0 20 0 0, wmin pref+20, wrap related"); | |
272 | + add(leftClick = Util.newComponent(JRadioButton.class, "leftClick"), "pad 0 20 0 0, wmin pref+20, wrap"); | |
273 | + | |
274 | + ButtonGroup group = new ButtonGroup(); | |
275 | + group.add(moveMouse); | |
276 | + group.add(leftClick); | |
277 | + | |
278 | + cursorControl.addActionListener(Util.getAction(this, "update")); | |
279 | + moveMouse.addActionListener(Util.getAction(this, "update")); | |
280 | + leftClick.addActionListener(Util.getAction(this, "update")); | |
281 | + | |
282 | + Application.getInstance(WiimoteWhiteboard.class).getMainFrame().addWindowFocusListener(new WindowFocusListener() { | |
283 | + public void windowGainedFocus(WindowEvent e) { | |
284 | + updateUI(); | |
285 | + } | |
286 | + public void windowLostFocus(WindowEvent e) {} | |
287 | + }); | |
288 | + | |
289 | + Util.getResourceMap(MainPanel.class).injectComponents(this); | |
290 | + } | |
291 | + | |
292 | + private JLabel addHeadline(String name) { | |
293 | + return addHeadline(name, true); | |
294 | + } | |
295 | + | |
296 | + private JLabel addHeadline(String name, boolean newline) { | |
297 | + final JLabel label = new JLabel(name); | |
298 | + label.setFont(label.getFont().deriveFont(Font.BOLD, 18f)); | |
299 | + add(label, (newline ? "newline unrelated, " : "") + "split, span"); | |
300 | + add(new JSeparator(), "growx, wrap related"); | |
301 | + return label; | |
302 | + } | |
303 | + | |
304 | + private class DotLabel extends JLabel { | |
305 | + | |
306 | + private boolean[] state = new boolean[WWPreferences.WIIMOTES]; | |
307 | + | |
308 | + public DotLabel(String name) { | |
309 | + setText(name); | |
310 | + setHorizontalAlignment(SwingConstants.CENTER); | |
311 | + setBorder(BorderFactory.createLineBorder(Color.lightGray)); | |
312 | + } | |
313 | + | |
314 | + public void update(Wiimote wiimote, boolean state) { | |
315 | + if (this.state[wiimote.getId() - 1] != state) { | |
316 | + this.state[wiimote.getId() - 1] = state; | |
317 | + repaint(); | |
318 | + } | |
319 | + } | |
320 | + | |
321 | + @Override | |
322 | + protected void paintComponent(Graphics g) { | |
323 | + final int n = Math.max(1, dh.getNumberOfConnectedWiimotes()); | |
324 | + for (int i = 1; i <= n; i++) { | |
325 | + g.setColor(state[i-1] ? WiimoteIcon.COLORS[i-1] : SystemColor.textInactiveText); | |
326 | + g.fillRect(0, (i-1)*this.getHeight()/n, this.getWidth(), this.getHeight()/n); | |
327 | + } | |
328 | + super.paintComponent(g); | |
329 | + } | |
330 | + | |
331 | + } | |
332 | +} | ... | ... |
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/gui/MainPanel.java~
0 → 100644
... | ... | @@ -0,0 +1,384 @@ |
1 | +/* | |
2 | + * Copyright (C) 2008-2009, Uwe Schmidt | |
3 | + * | |
4 | + * Permission is hereby granted, free of charge, to any person obtaining a | |
5 | + * copy of this software and associated documentation files (the "Software"), | |
6 | + * to deal in the Software without restriction, including without limitation | |
7 | + * the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
8 | + * and/or sell copies of the Software, and to permit persons to whom the | |
9 | + * Software is furnished to do so, subject to the following conditions: | |
10 | + * | |
11 | + * The above copyright notice and this permission notice shall be included in | |
12 | + * all copies or substantial portions of the Software. | |
13 | + * | |
14 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
15 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
16 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
17 | + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
18 | + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |
19 | + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | |
20 | + * DEALINGS IN THE SOFTWARE. | |
21 | + * | |
22 | + * The Software uses a third-party library (WiiRemoteJ) which is not part of | |
23 | + * the Software and is subject to its own license. | |
24 | + */ | |
25 | + | |
26 | +package org.uweschmidt.wiimote.whiteboard.gui; | |
27 | + | |
28 | +import java.awt.Color; | |
29 | +import java.awt.Font; | |
30 | +import java.awt.Graphics; | |
31 | +import java.awt.Point; | |
32 | +import java.awt.SystemColor; | |
33 | +import java.awt.event.ActionEvent; | |
34 | +import java.awt.event.ActionListener; | |
35 | +import java.awt.event.WindowEvent; | |
36 | +import java.awt.event.WindowFocusListener; | |
37 | +import java.util.Map; | |
38 | +import java.util.logging.Level; | |
39 | + | |
40 | +import javax.swing.BorderFactory; | |
41 | +import javax.swing.ButtonGroup; | |
42 | +import javax.swing.ImageIcon; | |
43 | +import javax.swing.JButton; | |
44 | +import javax.swing.JCheckBox; | |
45 | +import javax.swing.JLabel; | |
46 | +import javax.swing.JOptionPane; | |
47 | +import javax.swing.JPanel; | |
48 | +import javax.swing.JProgressBar; | |
49 | +import javax.swing.JRadioButton; | |
50 | +import javax.swing.JSeparator; | |
51 | +import javax.swing.SwingConstants; | |
52 | + | |
53 | +import net.miginfocom.swing.MigLayout; | |
54 | + | |
55 | +import org.jdesktop.application.Action; | |
56 | +import org.jdesktop.application.Application; | |
57 | +import org.jdesktop.application.ResourceMap; | |
58 | +import org.uweschmidt.wiimote.whiteboard.WiimoteDataHandler; | |
59 | +import org.uweschmidt.wiimote.whiteboard.WiimoteWhiteboard; | |
60 | +import org.uweschmidt.wiimote.whiteboard.WiimoteDataHandler.WiimoteDataListener; | |
61 | +import org.uweschmidt.wiimote.whiteboard.calibration.WiimoteCalibration; | |
62 | +import org.uweschmidt.wiimote.whiteboard.calibration.WiimoteCalibration.CalibrationEvent; | |
63 | +import org.uweschmidt.wiimote.whiteboard.calibration.WiimoteCalibration.CalibrationEventListener; | |
64 | +import org.uweschmidt.wiimote.whiteboard.ds.IRDot; | |
65 | +import org.uweschmidt.wiimote.whiteboard.ds.Wiimote; | |
66 | +import org.uweschmidt.wiimote.whiteboard.preferences.WWPreferences; | |
67 | +import org.uweschmidt.wiimote.whiteboard.util.Util; | |
68 | + | |
69 | +@SuppressWarnings("serial") | |
70 | +public class MainPanel extends JPanel implements WiimoteDataListener, CalibrationEventListener { | |
71 | + | |
72 | + private static final ImageIcon CALIBRATED = new ImageIcon(MainPanel.class.getResource("resources/icons/ok.png")); | |
73 | + private static final ImageIcon NOT_CALIBRATED = new ImageIcon(MainPanel.class.getResource("resources/icons/warning.png")); | |
74 | + | |
75 | + private static final WWPreferences prefs = WWPreferences.getPreferences(); | |
76 | + | |
77 | + private DotLabel[] dotLabel = new DotLabel[4]; | |
78 | + private JProgressBar[] batteryLevel = new JProgressBar[WWPreferences.WIIMOTES]; | |
79 | + private JButton[] resetButton = new JButton[WWPreferences.WIIMOTES]; | |
80 | + private WiimoteIcon[] wiimoteIcon = new WiimoteIcon[WWPreferences.WIIMOTES]; | |
81 | + private JLabel[] statusLabel = new JLabel[WWPreferences.WIIMOTES]; | |
82 | + private JCheckBox cursorControl; | |
83 | + private JRadioButton moveMouse, leftClick; | |
84 | + private JButton calibrationButton, cameraButton /*,warpedButton, calibrationInfoButton*/; | |
85 | + private ScreenSelector screenSelector; | |
86 | + | |
87 | + private boolean notifiedLowBattery = false; | |
88 | +// private boolean donePack = false; | |
89 | + | |
90 | + private Wiimote[] wiimotes = new Wiimote[WWPreferences.WIIMOTES]; | |
91 | + | |
92 | + private WiimoteDataHandler dh; | |
93 | + private WiimoteCalibration calibration; | |
94 | + private ResourceMap r = Util.getResourceMap(MainPanel.class); | |
95 | + | |
96 | + public MainPanel(WiimoteDataHandler dh, WiimoteCalibration calibration) { | |
97 | + this.dh = dh; | |
98 | + this.calibration = calibration; | |
99 | + | |
100 | + dh.addWiimoteDataListener(this); | |
101 | + calibration.addCalibrationEventListener(this); | |
102 | + | |
103 | + createComponents(); | |
104 | + | |
105 | + getValues(); | |
106 | + update(); | |
107 | + } | |
108 | + | |
109 | + public void getValues() { | |
110 | + cursorControl.setSelected(dh.isCursorControl()); | |
111 | +// rightClick.setSelected(prefs.isRightClick()); | |
112 | + leftClick.setSelected(prefs.isLeftClick()); | |
113 | + moveMouse.setSelected(!prefs.isLeftClick()); | |
114 | + } | |
115 | + | |
116 | + public void calibrationEvent(CalibrationEvent e) { | |
117 | + switch (e) { | |
118 | + case FINISHED: | |
119 | + case LOADED: | |
120 | + cursorControl.setSelected(WWPreferences.MOUSE_CONTROL_AFTER_CALIBRATION); | |
121 | + case STARTED: | |
122 | + case SCREEN_CHANGED: | |
123 | + case ABORTED: | |
124 | + update(); | |
125 | + break; | |
126 | + } | |
127 | + } | |
128 | + | |
129 | + public void batteryLevel(Wiimote wiimote, double level) { | |
130 | + batteryLevel[wiimote.getId()-1].setValue((int) Math.round(level * 100)); | |
131 | + batteryLevel[wiimote.getId()-1].setString(r.getString("batteryLevel", level * 100)); | |
132 | + | |
133 | + if (!notifiedLowBattery && prefs.isLowBatteryWarning() && dh.isConnected(wiimote) && level <= .05) { | |
134 | + notifiedLowBattery = true; | |
135 | + WiimoteWhiteboard.getLogger().log(Level.WARNING, r.getString("lowBattery")); | |
136 | + new Thread(new Runnable() { | |
137 | + public void run() { | |
138 | + JOptionPane.showMessageDialog(null, r.getString("lowBattery"), WiimoteWhiteboard.getProperty("id"), JOptionPane.WARNING_MESSAGE); | |
139 | + } | |
140 | + }).start(); | |
141 | + } | |
142 | + } | |
143 | + | |
144 | + public void irLights(Wiimote wiimote, IRDot[] lights) { | |
145 | + for (int i = 0; i < lights.length; i++) { | |
146 | + dotLabel[i].update(wiimote, lights[i] != null); | |
147 | + } | |
148 | + } | |
149 | + | |
150 | + public void irWarped(Map<Wiimote, IRDot[]> data, Point[] warped) { | |
151 | + } | |
152 | + | |
153 | + public void wiimoteConnected(Wiimote wiimote) { | |
154 | + wiimotes[wiimote.getId()-1] = wiimote; | |
155 | + | |
156 | + if (wiimote.getId() > 1) { | |
157 | + for (int i = 0; i < wiimote.getId(); i++) { | |
158 | + wiimoteIcon[i].setVisible(true); | |
159 | + } | |
160 | + } | |
161 | + statusLabel[wiimote.getId()-1].setVisible(true); | |
162 | + | |
163 | +// Application.getInstance(WiimoteWhiteboard.class).getMainFrame().pack(); | |
164 | + | |
165 | + batteryLevel[wiimote.getId()-1].setVisible(true); | |
166 | + resetButton[wiimote.getId()-1].setVisible(true); | |
167 | + cursorControl.setSelected(WWPreferences.MOUSE_CONTROL_AFTER_CALIBRATION); | |
168 | + update(); | |
169 | + } | |
170 | + | |
171 | + public void wiimoteDisconnected(Wiimote wiimote) { | |
172 | + if (wiimote.getId() != 1) batteryLevel[wiimote.getId()-1].setVisible(false); | |
173 | + if (wiimote.getId() != 1) wiimoteIcon[wiimote.getId()-1].setVisible(false); | |
174 | + if (wiimote.getId() != 1) resetButton[wiimote.getId()-1].setVisible(false); | |
175 | + if (wiimote.getId() != 1) statusLabel[wiimote.getId()-1].setVisible(false); | |
176 | + update(); | |
177 | +// Application.getInstance(WiimoteWhiteboard.class).getMainFrame().pack(); | |
178 | +// WiimoteWhiteboard.getLogger().log(Level.SEVERE, r.getString("disconnected", id)); | |
179 | + JOptionPane.showMessageDialog(null, r.getString("disconnected", wiimote.getId()), WiimoteWhiteboard.getProperty("id"), JOptionPane.ERROR_MESSAGE); | |
180 | + Application.getInstance(WiimoteWhiteboard.class).exit(); | |
181 | + } | |
182 | + | |
183 | + | |
184 | + /* | |
185 | + * UPDATE UI WIDGETS | |
186 | + */ | |
187 | + | |
188 | + @Action | |
189 | + public void update() { | |
190 | + calibrationButton.setEnabled(dh.isConnected()); | |
191 | + for (int i = 0; i < WWPreferences.WIIMOTES; i++) { | |
192 | + resetButton[i].setEnabled(dh.isConnected(wiimotes[i])); | |
193 | + wiimoteIcon[i].displayConnected(dh.isConnected(wiimotes[i])); | |
194 | + if (dh.isConnected(wiimotes[i])) { | |
195 | + boolean calibrated = calibration.isCalibrated(wiimotes[i]); | |
196 | + statusLabel[i].setText(r.getString(calibrated ? "calibrated" : "notCalibrated")); | |
197 | + statusLabel[i].setIcon(calibrated ? CALIBRATED : NOT_CALIBRATED); | |
198 | + } | |
199 | + } | |
200 | + cameraButton.setEnabled(dh.isConnected()); | |
201 | +// warpedButton.setEnabled(dh.isConnected() && calibration.isDone() && calibration.isAnyCalibrated(dh.getConnectedWiimotes())); | |
202 | + screenSelector.setEnabled(dh.isConnected() && !calibration.inProgress()); | |
203 | + | |
204 | + if (!dh.isConnected()) { | |
205 | + batteryLevel[0].setString(r.getString("searching")); | |
206 | + batteryLevel[0].setValue(0); | |
207 | + batteryLevel[0].setToolTipText(Util.getResourceMap(MainPanel.class).getString("batteryLevelBar.toolTipText")); | |
208 | + } else { | |
209 | + batteryLevel[0].setToolTipText(null); | |
210 | + } | |
211 | + | |
212 | + cursorControl.setEnabled(dh.isConnected() && calibration.isDone() && calibration.isAnyCalibrated(dh.getConnectedWiimotes())); | |
213 | + if (!cursorControl.isEnabled()) | |
214 | + cursorControl.setSelected(false); | |
215 | + | |
216 | + moveMouse.setEnabled(cursorControl.isSelected()); | |
217 | + leftClick.setEnabled(cursorControl.isSelected()); | |
218 | +// rightClick.setEnabled(cursorControl.isSelected() && leftClick.isSelected()); | |
219 | + | |
220 | + dh.setCursorControl(cursorControl.isSelected()); | |
221 | + prefs.setLeftClick(leftClick.isSelected()); | |
222 | +// prefs.setRightClick(/*rightClick.isEnabled() && */rightClick.isSelected()); | |
223 | + } | |
224 | + | |
225 | + /* | |
226 | + * MAIN PANEL | |
227 | + */ | |
228 | + | |
229 | + private void createComponents() { | |
230 | + setLayout(new MigLayout("nocache, hidemode 3, gap 0", "[fill]")); | |
231 | + | |
232 | + addHeadline(r.getString("wiimoteHeadline"), false); | |
233 | + | |
234 | + for (int i = 0; i < WWPreferences.WIIMOTES; i++) { | |
235 | + add(wiimoteIcon[i] = new WiimoteIcon(i+1), "split"); | |
236 | + if (i > 0) wiimoteIcon[i].setVisible(false); | |
237 | + } | |
238 | + | |
239 | + for (int i = 0; i < WWPreferences.WIIMOTES; i++) { | |
240 | + add(batteryLevel[i] = Util.newComponent(JProgressBar.class, "batteryLevelBar"), (i == 0 ? "newline related/2, " : "") + "split"); | |
241 | + if (i > 0) batteryLevel[i].setVisible(false); | |
242 | + } | |
243 | + | |
244 | + for (int i = 0; i < WWPreferences.WIIMOTES; i++) { | |
245 | + add(statusLabel[i] = Util.newComponent(JLabel.class, "statusLabel"), (i == 0 ? "newline related/2, split, " : "") + "sg, h 16!, center"); | |
246 | + statusLabel[i].setFont(statusLabel[i].getFont().deriveFont(10f)); | |
247 | + if (i > 0) statusLabel[i].setVisible(false); | |
248 | + } | |
249 | + | |
250 | + String gap = Util.MAC_OS_X_LEOPARD_OR_HIGHER ? "0" : "related/2"; | |
251 | + for (int i = 0; i < WWPreferences.WIIMOTES; i++) { | |
252 | + final int j = i; | |
253 | + add(resetButton[i] = Util.newComponent(JButton.class, "resetCameraButton"), (i == 0 ? "newline "+gap+", split, " : "") + ""); | |
254 | + resetButton[i].putClientProperty("JButton.buttonType", "textured"); | |
255 | + resetButton[i].putClientProperty("JComponent.sizeVariant", "small"); | |
256 | + resetButton[i].addActionListener(new ActionListener() { | |
257 | + public void actionPerformed(ActionEvent e) { | |
258 | + try { | |
259 | + dh.enableIR(wiimotes[j]); | |
260 | + } catch (Exception e1) { | |
261 | + e1.printStackTrace(); | |
262 | + } | |
263 | + } | |
264 | + }); | |
265 | + if (i > 0) resetButton[i].setVisible(false); | |
266 | + } | |
267 | + | |
268 | + add(new JSeparator(), String.format("newline %s, span, growx, wrap related", Util.MAC_OS_X_LEOPARD_OR_HIGHER ? "related/3" : "related")); | |
269 | + | |
270 | + add(Util.newComponent(JLabel.class, "visibleDotsLabel"), "split" + (Util.MAC_OS_X ? ", gapleft 4" : "")); | |
271 | + add(dotLabel[0] = new DotLabel("1"), "w 18, h 18"); | |
272 | + add(dotLabel[1] = new DotLabel("2"), "w 18, h 18"); | |
273 | + add(dotLabel[2] = new DotLabel("3"), "w 18, h 18"); | |
274 | + add(dotLabel[3] = new DotLabel("4"), "w 18, h 18, wrap related" + (Util.MAC_OS_X ? ", gapright 4" : "")); | |
275 | + | |
276 | + add(cameraButton = new JButton(Util.getAction(new CameraMonitor(dh), "monitor")), ""); | |
277 | + cameraButton.putClientProperty("JButton.buttonType", "textured"); | |
278 | + | |
279 | + // CALIBRATION | |
280 | + | |
281 | + addHeadline(r.getString("calibrationHeadline")); | |
282 | + | |
283 | +// add(warpedButton = new JButton(SC.getAction(new WarpedMonitor(dh, calibration), "monitor")), "wrap"); | |
284 | + | |
285 | + final JButton infoButton = new JButton(Util.getAction(new CalibrationInfoWindow(calibration, dh), "info")); | |
286 | +// final JToggleButton infoButton = new JToggleButton(Util.getAction(new CalibrationInfoWindow(calibration, dh), "info")); | |
287 | + infoButton.putClientProperty("JButton.buttonType", "textured"); | |
288 | + infoButton.putClientProperty("JComponent.sizeVariant", "small"); | |
289 | + add(infoButton, "wrap related"); | |
290 | +// add(new JSeparator(), "span, growx, wrap"); | |
291 | + | |
292 | + | |
293 | + add(screenSelector = new ScreenSelector(calibration, dh), "align center, grow 0, wrap related"); | |
294 | + | |
295 | + add(calibrationButton = Util.newComponent(JButton.class, "calibrationButton"), ""); | |
296 | + calibrationButton.putClientProperty("JButton.buttonType", "textured"); | |
297 | + calibrationButton.addActionListener(new ActionListener() { | |
298 | + public void actionPerformed(ActionEvent e) { | |
299 | + calibration.start(dh.getConnectedWiimotes()); | |
300 | + } | |
301 | + }); | |
302 | + | |
303 | + // MOUSE CONTROL | |
304 | + | |
305 | + addHeadline(r.getString("mouseControlHeadline")); | |
306 | + | |
307 | + add(cursorControl = Util.newComponent(JCheckBox.class, "cursorControl"), "wrap related"); | |
308 | + add(moveMouse = Util.newComponent(JRadioButton.class, "moveMouse"), "pad 0 20 0 0, wmin pref+20, wrap related"); | |
309 | + add(leftClick = Util.newComponent(JRadioButton.class, "leftClick"), "pad 0 20 0 0, wmin pref+20, wrap"); | |
310 | +// add(rightClick = SC.newComponent(JCheckBox.class, "rightClick"), "pad 0 20 0 0, wrap"); | |
311 | + | |
312 | +// add(new JSeparator(), "wrap"); | |
313 | +// final JButton exitButton = new JButton(SC.getAction(Application.getInstance(), "quitApp")); | |
314 | +// add(exitButton, "wrap"); | |
315 | + | |
316 | + ButtonGroup group = new ButtonGroup(); | |
317 | + group.add(moveMouse); | |
318 | + group.add(leftClick); | |
319 | + | |
320 | + cursorControl.addActionListener(Util.getAction(this, "update")); | |
321 | + moveMouse.addActionListener(Util.getAction(this, "update")); | |
322 | + leftClick.addActionListener(Util.getAction(this, "update")); | |
323 | +// rightClick.addActionListener(SC.getAction(this, "update")); | |
324 | + | |
325 | + // fixes issue that components stay grayed out although they're enabled | |
326 | + // (mac os x), only happens when mainFrame doesn't have focus on connect | |
327 | + // workaround for windows: pack() once frame gains focus, due to | |
328 | + // incorrect height apparently caused by non-resizability | |
329 | + Application.getInstance(WiimoteWhiteboard.class).getMainFrame().addWindowFocusListener(new WindowFocusListener() { | |
330 | + public void windowGainedFocus(WindowEvent e) { | |
331 | + updateUI(); | |
332 | +// if (!Util.MAC_OS_X && !donePack) { | |
333 | +// donePack = true; | |
334 | +// Application.getInstance(WiimoteWhiteboard.class).getMainFrame().pack(); | |
335 | +// } | |
336 | + } | |
337 | + public void windowLostFocus(WindowEvent e) {} | |
338 | + }); | |
339 | + | |
340 | + Util.getResourceMap(MainPanel.class).injectComponents(this); | |
341 | + } | |
342 | + | |
343 | + private JLabel addHeadline(String name) { | |
344 | + return addHeadline(name, true); | |
345 | + } | |
346 | + | |
347 | + private JLabel addHeadline(String name, boolean newline) { | |
348 | + final JLabel label = new JLabel(name); | |
349 | + label.setFont(label.getFont().deriveFont(Font.BOLD, 18f)); | |
350 | + add(label, (newline ? "newline unrelated, " : "") + "split, span"); | |
351 | + add(new JSeparator(), "growx, wrap related"); | |
352 | + return label; | |
353 | + } | |
354 | + | |
355 | + private class DotLabel extends JLabel { | |
356 | + | |
357 | + private boolean[] state = new boolean[WWPreferences.WIIMOTES]; | |
358 | + | |
359 | + public DotLabel(String name) { | |
360 | + setText(name); | |
361 | + setHorizontalAlignment(SwingConstants.CENTER); | |
362 | + setBorder(BorderFactory.createLineBorder(Color.lightGray)); | |
363 | + } | |
364 | + | |
365 | + public void update(Wiimote wiimote, boolean state) { | |
366 | + if (this.state[wiimote.getId() - 1] != state) { | |
367 | + this.state[wiimote.getId() - 1] = state; | |
368 | + repaint(); | |
369 | + } | |
370 | + } | |
371 | + | |
372 | + @Override | |
373 | + protected void paintComponent(Graphics g) { | |
374 | + final int n = Math.max(1, dh.getNumberOfConnectedWiimotes()); | |
375 | + for (int i = 1; i <= n; i++) { | |
376 | + g.setColor(state[i-1] ? WiimoteIcon.COLORS[i-1] : SystemColor.textInactiveText); | |
377 | + g.fillRect(0, (i-1)*this.getHeight()/n, this.getWidth(), this.getHeight()/n); | |
378 | +// g.fillRect((i-1)*this.getWidth()/n, 0, this.getWidth()/n, this.getHeight()); | |
379 | + } | |
380 | + super.paintComponent(g); | |
381 | + } | |
382 | + | |
383 | + } | |
384 | +} | ... | ... |
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/gui/MenuBar.java
0 → 100644
... | ... | @@ -0,0 +1,48 @@ |
1 | +package org.mote.wiimote.whiteboard.gui; | |
2 | + | |
3 | +import java.awt.Toolkit; | |
4 | +import java.awt.event.KeyEvent; | |
5 | + | |
6 | +import javax.swing.JMenu; | |
7 | +import javax.swing.JMenuBar; | |
8 | +import javax.swing.JMenuItem; | |
9 | +import javax.swing.KeyStroke; | |
10 | + | |
11 | +import org.jdesktop.application.Application; | |
12 | +import org.mote.wiimote.whiteboard.WiimoteWhiteboard; | |
13 | +import org.mote.wiimote.whiteboard.util.Util; | |
14 | + | |
15 | +@SuppressWarnings("serial") | |
16 | +public class MenuBar extends JMenuBar { | |
17 | + | |
18 | + public MenuBar(PreferencesWindow pf, AboutWindow af, /*HelpHandler hh*/, LogWindow lw) { | |
19 | + JMenu menu; | |
20 | + JMenuItem item; | |
21 | + | |
22 | + if (!Util.MAC_OS_X) { | |
23 | + menu = new JMenu(Util.getResourceMap(MenuBar.class).getString("editMenu")); | |
24 | + menu.add(new JMenuItem(Util.getAction(pf, "preferences"))); | |
25 | + add(menu); | |
26 | + } | |
27 | + | |
28 | + //menu = new JMenu(Util.getResourceMap(HelpHandler.class).getString("help")); | |
29 | + | |
30 | + //menu.add(item = new JMenuItem(Util.getAction(hh, "help"))); | |
31 | + if (Util.MAC_OS_X) | |
32 | + item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_SLASH, KeyEvent.SHIFT_MASK | Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); | |
33 | + | |
34 | + //menu.addSeparator(); | |
35 | + | |
36 | + //if (!Util.MAC_OS_X) | |
37 | + // menu.add(new JMenuItem(Util.getAction(af, "about"))); | |
38 | + | |
39 | + //menu.add(new JMenuItem(Util.getAction(lw, "log"))); | |
40 | + | |
41 | + //menu.addSeparator(); | |
42 | + | |
43 | + //menu.add(new JMenuItem(Util.getAction(Application.getInstance(WiimoteWhiteboard.class), "donate"))); | |
44 | + | |
45 | + add(menu); | |
46 | + } | |
47 | + | |
48 | +} | ... | ... |
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/gui/MenuBar.java~
0 → 100644
... | ... | @@ -0,0 +1,48 @@ |
1 | +package org.mote.wiimote.whiteboard.gui; | |
2 | + | |
3 | +import java.awt.Toolkit; | |
4 | +import java.awt.event.KeyEvent; | |
5 | + | |
6 | +import javax.swing.JMenu; | |
7 | +import javax.swing.JMenuBar; | |
8 | +import javax.swing.JMenuItem; | |
9 | +import javax.swing.KeyStroke; | |
10 | + | |
11 | +import org.jdesktop.application.Application; | |
12 | +import org.mote.wiimote.whiteboard.WiimoteWhiteboard; | |
13 | +import org.mote.wiimote.whiteboard.util.Util; | |
14 | + | |
15 | +@SuppressWarnings("serial") | |
16 | +public class MenuBar extends JMenuBar { | |
17 | + | |
18 | + public MenuBar(PreferencesWindow pf, AboutWindow af, /*HelpHandler hh*/, LogWindow lw) { | |
19 | + JMenu menu; | |
20 | + JMenuItem item; | |
21 | + | |
22 | + if (!Util.MAC_OS_X) { | |
23 | + menu = new JMenu(Util.getResourceMap(MenuBar.class).getString("editMenu")); | |
24 | + menu.add(new JMenuItem(Util.getAction(pf, "preferences"))); | |
25 | + add(menu); | |
26 | + } | |
27 | + | |
28 | + //menu = new JMenu(Util.getResourceMap(HelpHandler.class).getString("help")); | |
29 | + | |
30 | + //menu.add(item = new JMenuItem(Util.getAction(hh, "help"))); | |
31 | + if (Util.MAC_OS_X) | |
32 | + item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_SLASH, KeyEvent.SHIFT_MASK | Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); | |
33 | + | |
34 | + //menu.addSeparator(); | |
35 | + | |
36 | + //if (!Util.MAC_OS_X) | |
37 | + // menu.add(new JMenuItem(Util.getAction(af, "about"))); | |
38 | + | |
39 | + //menu.add(new JMenuItem(Util.getAction(lw, "log"))); | |
40 | + | |
41 | + //menu.addSeparator(); | |
42 | + | |
43 | + //menu.add(new JMenuItem(Util.getAction(Application.getInstance(WiimoteWhiteboard.class), "donate"))); | |
44 | + | |
45 | + add(menu); | |
46 | + } | |
47 | + | |
48 | +} | ... | ... |
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/gui/PreferencesWindow.java
0 → 100644
... | ... | @@ -0,0 +1,342 @@ |
1 | +package org.mote.wiimote.whiteboard.gui; | |
2 | +import java.awt.BorderLayout; | |
3 | +import java.awt.FlowLayout; | |
4 | +import java.awt.Insets; | |
5 | +import java.awt.event.ActionEvent; | |
6 | +import java.awt.event.ActionListener; | |
7 | +import java.awt.event.ItemEvent; | |
8 | +import java.awt.event.ItemListener; | |
9 | +import java.awt.event.WindowEvent; | |
10 | +import java.awt.event.WindowFocusListener; | |
11 | +import java.net.InetAddress; | |
12 | +import java.net.UnknownHostException; | |
13 | +import java.util.Dictionary; | |
14 | +import java.util.Hashtable; | |
15 | + | |
16 | +import javax.swing.BorderFactory; | |
17 | +import javax.swing.ButtonGroup; | |
18 | +import javax.swing.DefaultComboBoxModel; | |
19 | +import javax.swing.JButton; | |
20 | +import javax.swing.JCheckBox; | |
21 | +import javax.swing.JComboBox; | |
22 | +import javax.swing.JDialog; | |
23 | +import javax.swing.JLabel; | |
24 | +import javax.swing.JOptionPane; | |
25 | +import javax.swing.JPanel; | |
26 | +import javax.swing.JRadioButton; | |
27 | +import javax.swing.JSeparator; | |
28 | +import javax.swing.JSlider; | |
29 | +import javax.swing.JTabbedPane; | |
30 | +import javax.swing.JTextField; | |
31 | +import javax.swing.UIManager; | |
32 | +import javax.swing.border.Border; | |
33 | +import javax.swing.border.TitledBorder; | |
34 | +import javax.swing.event.ChangeEvent; | |
35 | +import javax.swing.event.ChangeListener; | |
36 | + | |
37 | +import net.miginfocom.swing.MigLayout; | |
38 | + | |
39 | +import org.jdesktop.application.Action; | |
40 | +import org.jdesktop.application.Application; | |
41 | +import org.jdesktop.application.ResourceMap; | |
42 | +import org.mote.wiimote.whiteboard.WiimoteWhiteboard; | |
43 | +import org.mote.wiimote.whiteboard.mouse.DefaultControlStrategy; | |
44 | +import org.mote.wiimote.whiteboard.mouse.TouchpadControlStrategy; | |
45 | +import org.mote.wiimote.whiteboard.mouse.smoothing.AdaptiveExponentialSmoothing; | |
46 | +import org.mote.wiimote.whiteboard.mouse.smoothing.NoSmoothing; | |
47 | +import org.mote.wiimote.whiteboard.mouse.smoothing.SimpleMovingAverage; | |
48 | +import org.mote.wiimote.whiteboard.preferences.WWPreferences; | |
49 | +import org.mote.wiimote.whiteboard.preferences.WWPreferences.LocaleWrapper; | |
50 | +import org.mote.wiimote.whiteboard.util.Util; | |
51 | + | |
52 | +@SuppressWarnings("serial") | |
53 | +public class PreferencesWindow extends JDialog { | |
54 | + | |
55 | + private final static WWPreferences prefs = WWPreferences.getPreferences(); | |
56 | + private final static ResourceMap r = Util.getResourceMap(PreferencesWindow.class); | |
57 | + | |
58 | + private JSlider delaySlider; | |
59 | + private JTextField tuioHost; | |
60 | + private JButton defaultsButton; | |
61 | + private JCheckBox batteryWarning, checkForUpdates, tuioEnable, mouseSmoothing, touchpadMode, rightClicks, assistDoubleClicks; | |
62 | + private JRadioButton staticSmoothing, adaptiveSmoothing; | |
63 | + private JComboBox languages; | |
64 | + private JTabbedPane tabbedPane; | |
65 | + private boolean donePack = false; | |
66 | + | |
67 | + public PreferencesWindow(final MainPanel mp, final HelpHandler hh) { | |
68 | + super(Application.getInstance(WiimoteWhiteboard.class).getMainFrame(), r.getString("preferences.Action.text")); | |
69 | + | |
70 | + Border aquaBorder = UIManager.getBorder("TitledBorder.aquaVariant"); | |
71 | + if (aquaBorder != null) UIManager.put("TitledBorder.border", aquaBorder); | |
72 | + | |
73 | + // create panels | |
74 | + tabbedPane = new JTabbedPane(); | |
75 | + JPanel generalPanel = new JPanel(new MigLayout(Util.MAC_OS_X ? "insets para-10 para para para" : "")); | |
76 | + JPanel mousePanel = new JPanel(new MigLayout(Util.MAC_OS_X ? String.format("insets para-10 para %s para", Util.MAC_OS_X_LEOPARD_OR_HIGHER ? "para-10" : "para") : "")); | |
77 | + JPanel tuioPanel = new JPanel(new MigLayout(Util.MAC_OS_X ? "insets para-10 para para para" : "")); | |
78 | + generalPanel.setOpaque(false); | |
79 | + mousePanel.setOpaque(false); | |
80 | + tuioPanel.setOpaque(false); | |
81 | + tabbedPane.addTab(r.getString("generalTab"), generalPanel); | |
82 | + tabbedPane.addTab(r.getString("mouseTab"), mousePanel); | |
83 | + tabbedPane.addTab(r.getString("tuioTab"), tuioPanel); | |
84 | + | |
85 | + ((JPanel)getContentPane()).setBorder(BorderFactory.createEmptyBorder(8, 10, 10, 10)); | |
86 | + add(tabbedPane, BorderLayout.CENTER); | |
87 | + | |
88 | + /* | |
89 | + * GENERAL PANEL | |
90 | + */ | |
91 | + | |
92 | + // check for updates | |
93 | + generalPanel.add(checkForUpdates = Util.newComponent(JCheckBox.class, "checkForUpdates"), "wrap"); | |
94 | + checkForUpdates.addActionListener(new ActionListener() { | |
95 | + public void actionPerformed(ActionEvent arg0) { | |
96 | + prefs.setCheckForUpdates(checkForUpdates.isSelected()); | |
97 | + } | |
98 | + }); | |
99 | + | |
100 | + // low battery warning | |
101 | + generalPanel.add(batteryWarning = Util.newComponent(JCheckBox.class, "batteryWarning"), "wrap"); | |
102 | + batteryWarning.addActionListener(new ActionListener() { | |
103 | + public void actionPerformed(ActionEvent arg0) { | |
104 | + prefs.setLowBatteryWarning(batteryWarning.isSelected()); | |
105 | + } | |
106 | + }); | |
107 | + | |
108 | + generalPanel.add(new JSeparator(), "split, span, pushx, growx, wrap"); | |
109 | + | |
110 | + generalPanel.add(Util.newComponent(JLabel.class, "language"), "split, gapbottom 3, gapleft 6"); | |
111 | + generalPanel.add(languages = Util.newComponent(JComboBox.class, "languageBox"), "wrap"); | |
112 | + languages.setModel(new DefaultComboBoxModel(WWPreferences.LANGUAGES)); | |
113 | + languages.addItemListener(new ItemListener() { | |
114 | + public void itemStateChanged(ItemEvent e) { | |
115 | + if (e.getStateChange() == ItemEvent.SELECTED) { | |
116 | + prefs.setLanguage(((LocaleWrapper)languages.getSelectedItem()).getLocaleString()); | |
117 | + } | |
118 | + } | |
119 | + }); | |
120 | + generalPanel.add(Util.newComponent(JLabel.class, "languageRestartLabel"), "split, span, w 225, gapleft 6"); | |
121 | + | |
122 | + /* | |
123 | + * MOUSE CONTROL PANEL | |
124 | + */ | |
125 | + | |
126 | + // touchpad mode | |
127 | + mousePanel.add(touchpadMode = Util.newComponent(JCheckBox.class, "touchpadMode"), "wrap"); | |
128 | + touchpadMode.addActionListener(new ActionListener() { | |
129 | + public void actionPerformed(ActionEvent arg0) { | |
130 | + prefs.setCursorControl(touchpadMode.isSelected() ? TouchpadControlStrategy.class.getName() : DefaultControlStrategy.class.getName()); | |
131 | + } | |
132 | + }); | |
133 | + | |
134 | + mousePanel.add(assistDoubleClicks = Util.newComponent(JCheckBox.class, "assistDoubleClicks"), "wrap"); | |
135 | + assistDoubleClicks.addActionListener(new ActionListener() { | |
136 | + public void actionPerformed(ActionEvent arg0) { | |
137 | + prefs.setAssistDoubleClicks(assistDoubleClicks.isSelected()); | |
138 | + } | |
139 | + }); | |
140 | + | |
141 | + // mouse smoothing | |
142 | + final ButtonGroup smoothingGroup = new ButtonGroup(); | |
143 | + mousePanel.add(mouseSmoothing = Util.newComponent(JCheckBox.class, "mouseSmoothing"), "split"); | |
144 | + mouseSmoothing.addActionListener(new ActionListener() { | |
145 | + public void actionPerformed(ActionEvent arg0) { | |
146 | + staticSmoothing.setEnabled(mouseSmoothing.isSelected()); | |
147 | + adaptiveSmoothing.setEnabled(mouseSmoothing.isSelected()); | |
148 | + if (mouseSmoothing.isSelected()) { | |
149 | + if (smoothingGroup.getSelection() == null) | |
150 | + adaptiveSmoothing.doClick(); | |
151 | + else { | |
152 | + if (adaptiveSmoothing.isSelected()) adaptiveSmoothing.doClick(); | |
153 | + if (staticSmoothing.isSelected()) staticSmoothing.doClick(); | |
154 | + } | |
155 | + } else { | |
156 | + prefs.setMouseSmoothing(NoSmoothing.class.getName()); | |
157 | + } | |
158 | + } | |
159 | + }); | |
160 | + | |
161 | + mousePanel.add(staticSmoothing = Util.newComponent(JRadioButton.class, "staticSmoothing")); | |
162 | + staticSmoothing.addActionListener(new ActionListener() { | |
163 | + public void actionPerformed(ActionEvent e) { | |
164 | + prefs.setMouseSmoothing(SimpleMovingAverage.class.getName()); | |
165 | + } | |
166 | + }); | |
167 | + smoothingGroup.add(staticSmoothing); | |
168 | + | |
169 | + mousePanel.add(adaptiveSmoothing = Util.newComponent(JRadioButton.class, "adaptiveSmoothing")); | |
170 | + adaptiveSmoothing.addActionListener(new ActionListener() { | |
171 | + public void actionPerformed(ActionEvent e) { | |
172 | + prefs.setMouseSmoothing(AdaptiveExponentialSmoothing.class.getName()); | |
173 | + } | |
174 | + }); | |
175 | + smoothingGroup.add(adaptiveSmoothing); | |
176 | + | |
177 | + | |
178 | + | |
179 | + mousePanel.add(new JSeparator(), "newline, split, span, pushx, growx, wrap"); | |
180 | + | |
181 | + // right clicks | |
182 | + mousePanel.add(rightClicks = Util.newComponent(JCheckBox.class, "rightClicks"), "wrap"); | |
183 | + rightClicks.addActionListener(new ActionListener() { | |
184 | + public void actionPerformed(ActionEvent arg0) { | |
185 | + prefs.setRightClick(rightClicks.isSelected()); | |
186 | + } | |
187 | + }); | |
188 | + | |
189 | + // right click activation delay | |
190 | + JPanel wrapper = new JPanel(new FlowLayout(FlowLayout.CENTER, 0, 0)); | |
191 | + wrapper.setOpaque(false); | |
192 | + final TitledBorder tb = BorderFactory.createTitledBorder(null, r.getString("rightClickDelay"), TitledBorder.CENTER, TitledBorder.DEFAULT_POSITION, new JLabel().getFont().deriveFont(11f)); | |
193 | + wrapper.setBorder(tb); | |
194 | + delaySlider = Util.newComponent(JSlider.class, "delaySlider"); | |
195 | + Dictionary<Integer, JLabel> labelTable = new Hashtable<Integer, JLabel>(); | |
196 | + labelTable.put(500, new JLabel("\u00bd")); | |
197 | + labelTable.put(1000, new JLabel("1")); | |
198 | + labelTable.put(2000, new JLabel("2")); | |
199 | + labelTable.put(3000, new JLabel("3")); | |
200 | + delaySlider.setLabelTable(labelTable); | |
201 | + delaySlider.addChangeListener(new ChangeListener() { | |
202 | + public void stateChanged(ChangeEvent e) { | |
203 | + if (!delaySlider.getValueIsAdjusting() && isVisible()) { | |
204 | + prefs.setRightClickDelay(delaySlider.getValue()); | |
205 | + } | |
206 | + } | |
207 | + }); | |
208 | + Insets tbInsets = tb.getBorderInsets(wrapper); | |
209 | + int tbWidth = tb.getMinimumSize(wrapper).width-tbInsets.left-tbInsets.right+(Util.MAC_OS_X_LEOPARD_OR_HIGHER ? 0 : 20); | |
210 | + wrapper.add(delaySlider); | |
211 | + mousePanel.add(wrapper, "center, growx, w " +tbWidth); | |
212 | + | |
213 | + /* | |
214 | + * TUIO/OSC PANEL | |
215 | + */ | |
216 | + | |
217 | + // enable tuio | |
218 | + tuioPanel.add(tuioEnable = Util.newComponent(JCheckBox.class, "tuioEnable"), "wrap"); | |
219 | + tuioEnable.addActionListener(new ActionListener() { | |
220 | + public void actionPerformed(ActionEvent arg0) { | |
221 | + if (tuioEnable.isSelected()) { | |
222 | + String[] s = tuioHost.getText().split(":"); | |
223 | + String errorTitle = null; | |
224 | + if ((s.length == 1 && s[0].length() == 0) || s.length > 2) { | |
225 | + errorTitle = r.getString("tuioInvalidFormat"); | |
226 | + } else { | |
227 | + try { | |
228 | + int port = 3333; | |
229 | + if (s.length == 2) { | |
230 | + port = Integer.valueOf(s[1]); | |
231 | + if (port < 0 || port > 0xFFFF) { | |
232 | + throw new NumberFormatException(); | |
233 | + } | |
234 | + } | |
235 | + InetAddress.getByName(s[0]); | |
236 | + | |
237 | + prefs.setTuioPort(port); | |
238 | + prefs.setTuioHost(s[0]); | |
239 | + prefs.setTuioEnabled(true); | |
240 | + } catch (NumberFormatException e) { | |
241 | + errorTitle = r.getString("tuioInvalidPort", s[1]); | |
242 | + } catch (UnknownHostException e) { | |
243 | + errorTitle = r.getString("tuioInvalidHost", s[0]); | |
244 | + } | |
245 | + } | |
246 | + | |
247 | + if (errorTitle != null) { | |
248 | + JOptionPane.showMessageDialog(PreferencesWindow.this, r.getString("tuioErrorMessage"), errorTitle, JOptionPane.ERROR_MESSAGE); | |
249 | + } | |
250 | + | |
251 | + } else { | |
252 | + prefs.setTuioEnabled(false); | |
253 | + } | |
254 | + update(); | |
255 | + } | |
256 | + }); | |
257 | + | |
258 | + // tuio host | |
259 | + tuioPanel.add(tuioHost = Util.newComponent(JTextField.class, "tuioHost"), "growx, pushx, wrap"); | |
260 | + | |
261 | + | |
262 | + JPanel bottomPanel = new JPanel(new MigLayout(Util.MAC_OS_X ? "insets 0 2 0 2, gap 0" : "insets i 0 0 0, gap 0")); | |
263 | + add(bottomPanel, BorderLayout.SOUTH); | |
264 | + | |
265 | + bottomPanel.add(defaultsButton = Util.newComponent(JButton.class, "resetButton")); | |
266 | + defaultsButton.addActionListener(new ActionListener() { | |
267 | + public void actionPerformed(ActionEvent e) { | |
268 | + prefs.reset(); | |
269 | + mp.update(); | |
270 | + update(); | |
271 | + } | |
272 | + }); | |
273 | + | |
274 | + JButton helpButton = new JButton(Util.getAction(hh, "help")); | |
275 | + bottomPanel.add(helpButton, "push, right"); | |
276 | + if (Util.MAC_OS_X_LEOPARD_OR_HIGHER) { | |
277 | + helpButton.setText(""); | |
278 | + helpButton.putClientProperty("JButton.buttonType", "help"); | |
279 | + } else { | |
280 | + helpButton.setText(Util.getResourceMap(HelpHandler.class).getString("help")); | |
281 | + } | |
282 | + | |
283 | + Util.getResourceMap(PreferencesWindow.class).injectComponents(this); | |
284 | + | |
285 | + pack(); | |
286 | + setResizable(false); | |
287 | + | |
288 | + if (Util.WINDOWS) { | |
289 | + addWindowFocusListener(new WindowFocusListener() { | |
290 | + public void windowGainedFocus(WindowEvent e) { | |
291 | + if (!donePack) { | |
292 | + donePack = true; | |
293 | + pack(); | |
294 | + Util.placeDialogWindow(PreferencesWindow.this, getWidth(), getHeight()); | |
295 | + } | |
296 | + } | |
297 | + public void windowLostFocus(WindowEvent e) {} | |
298 | + }); | |
299 | + } | |
300 | + } | |
301 | + | |
302 | + private void update() { | |
303 | + checkForUpdates.setSelected(prefs.checkForUpdates()); | |
304 | + batteryWarning.setSelected(prefs.isLowBatteryWarning()); | |
305 | + rightClicks.setSelected(prefs.isRightClick()); | |
306 | + assistDoubleClicks.setSelected(prefs.assistDoubleClicks()); | |
307 | + delaySlider.setValue((int)prefs.getRightClickDelay()); | |
308 | + tuioEnable.setSelected(prefs.isTuioEnabled()); | |
309 | + tuioHost.setText(String.format("%s:%d", prefs.getTuioHost(), prefs.getTuioPort())); | |
310 | + tuioHost.setEnabled(!tuioEnable.isSelected()); | |
311 | + tuioHost.setToolTipText(tuioHost.isEnabled() ? Util.getResourceMap(PreferencesWindow.class).getString("tuioHost.toolTipText") : r.getString("tuioDisableToEdit")); | |
312 | + | |
313 | + touchpadMode.setSelected(TouchpadControlStrategy.class.getName().equals(prefs.getCursorControl())); | |
314 | + | |
315 | + mouseSmoothing.setSelected(!NoSmoothing.class.getName().equals(prefs.getMouseSmoothing())); | |
316 | + adaptiveSmoothing.setSelected(true); | |
317 | + staticSmoothing.setSelected(SimpleMovingAverage.class.getName().equals(prefs.getMouseSmoothing())); | |
318 | + adaptiveSmoothing.setEnabled(mouseSmoothing.isSelected()); | |
319 | + staticSmoothing.setEnabled(mouseSmoothing.isSelected()); | |
320 | + | |
321 | + String lang = prefs.getLanguage(); | |
322 | + for (int i = 0; i < languages.getItemCount(); i++) { | |
323 | + LocaleWrapper lw = (LocaleWrapper)languages.getItemAt(i); | |
324 | + if (lang.equals(lw.getLocaleString())) { | |
325 | + languages.setSelectedIndex(i); | |
326 | + break; | |
327 | + } | |
328 | + } | |
329 | + } | |
330 | + | |
331 | + @Action | |
332 | + public void preferences() { | |
333 | + if (!isVisible()) { | |
334 | + update(); | |
335 | + pack(); | |
336 | + | |
337 | + Util.placeDialogWindow(this, getWidth(), getHeight()); | |
338 | + } | |
339 | + setVisible(true); | |
340 | + } | |
341 | + | |
342 | +} | ... | ... |
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/gui/PreferencesWindow.java~
0 → 100644
... | ... | @@ -0,0 +1,396 @@ |
1 | +/* | |
2 | + * Copyright (C) 2008-2009, Uwe Schmidt | |
3 | + * | |
4 | + * Permission is hereby granted, free of charge, to any person obtaining a | |
5 | + * copy of this software and associated documentation files (the "Software"), | |
6 | + * to deal in the Software without restriction, including without limitation | |
7 | + * the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
8 | + * and/or sell copies of the Software, and to permit persons to whom the | |
9 | + * Software is furnished to do so, subject to the following conditions: | |
10 | + * | |
11 | + * The above copyright notice and this permission notice shall be included in | |
12 | + * all copies or substantial portions of the Software. | |
13 | + * | |
14 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
15 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
16 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
17 | + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
18 | + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |
19 | + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | |
20 | + * DEALINGS IN THE SOFTWARE. | |
21 | + * | |
22 | + * The Software uses a third-party library (WiiRemoteJ) which is not part of | |
23 | + * the Software and is subject to its own license. | |
24 | + */ | |
25 | + | |
26 | +package org.uweschmidt.wiimote.whiteboard.gui; | |
27 | +import java.awt.BorderLayout; | |
28 | +import java.awt.FlowLayout; | |
29 | +import java.awt.Insets; | |
30 | +import java.awt.event.ActionEvent; | |
31 | +import java.awt.event.ActionListener; | |
32 | +import java.awt.event.ItemEvent; | |
33 | +import java.awt.event.ItemListener; | |
34 | +import java.awt.event.WindowEvent; | |
35 | +import java.awt.event.WindowFocusListener; | |
36 | +import java.net.InetAddress; | |
37 | +import java.net.UnknownHostException; | |
38 | +import java.util.Dictionary; | |
39 | +import java.util.Hashtable; | |
40 | + | |
41 | +import javax.swing.BorderFactory; | |
42 | +import javax.swing.ButtonGroup; | |
43 | +import javax.swing.DefaultComboBoxModel; | |
44 | +import javax.swing.JButton; | |
45 | +import javax.swing.JCheckBox; | |
46 | +import javax.swing.JComboBox; | |
47 | +import javax.swing.JDialog; | |
48 | +import javax.swing.JLabel; | |
49 | +import javax.swing.JOptionPane; | |
50 | +import javax.swing.JPanel; | |
51 | +import javax.swing.JRadioButton; | |
52 | +import javax.swing.JSeparator; | |
53 | +import javax.swing.JSlider; | |
54 | +import javax.swing.JTabbedPane; | |
55 | +import javax.swing.JTextField; | |
56 | +import javax.swing.UIManager; | |
57 | +import javax.swing.border.Border; | |
58 | +import javax.swing.border.TitledBorder; | |
59 | +import javax.swing.event.ChangeEvent; | |
60 | +import javax.swing.event.ChangeListener; | |
61 | + | |
62 | +import net.miginfocom.swing.MigLayout; | |
63 | + | |
64 | +import org.jdesktop.application.Action; | |
65 | +import org.jdesktop.application.Application; | |
66 | +import org.jdesktop.application.ResourceMap; | |
67 | +import org.uweschmidt.wiimote.whiteboard.WiimoteWhiteboard; | |
68 | +import org.uweschmidt.wiimote.whiteboard.mouse.DefaultControlStrategy; | |
69 | +import org.uweschmidt.wiimote.whiteboard.mouse.TouchpadControlStrategy; | |
70 | +import org.uweschmidt.wiimote.whiteboard.mouse.smoothing.AdaptiveExponentialSmoothing; | |
71 | +import org.uweschmidt.wiimote.whiteboard.mouse.smoothing.NoSmoothing; | |
72 | +import org.uweschmidt.wiimote.whiteboard.mouse.smoothing.SimpleMovingAverage; | |
73 | +import org.uweschmidt.wiimote.whiteboard.preferences.WWPreferences; | |
74 | +import org.uweschmidt.wiimote.whiteboard.preferences.WWPreferences.LocaleWrapper; | |
75 | +import org.uweschmidt.wiimote.whiteboard.util.Util; | |
76 | + | |
77 | +@SuppressWarnings("serial") | |
78 | +public class PreferencesWindow extends JDialog { | |
79 | + | |
80 | + private final static WWPreferences prefs = WWPreferences.getPreferences(); | |
81 | + private final static ResourceMap r = Util.getResourceMap(PreferencesWindow.class); | |
82 | + | |
83 | + private JSlider delaySlider; | |
84 | + private JTextField tuioHost; | |
85 | + private JButton defaultsButton; | |
86 | + private JCheckBox batteryWarning, checkForUpdates, tuioEnable, mouseSmoothing, touchpadMode, rightClicks, assistDoubleClicks; | |
87 | + private JRadioButton staticSmoothing, adaptiveSmoothing; | |
88 | +// private JRadioButton[] wiimoteNumberButtons = new JRadioButton[WWPreferences.WIIMOTES]; | |
89 | + private JComboBox languages; | |
90 | + private JTabbedPane tabbedPane; | |
91 | + private boolean donePack = false; | |
92 | + | |
93 | + public PreferencesWindow(final MainPanel mp, final HelpHandler hh) { | |
94 | + super(Application.getInstance(WiimoteWhiteboard.class).getMainFrame(), r.getString("preferences.Action.text")); | |
95 | + | |
96 | + // use NSBox-like TitledBorder if available (Leopard) | |
97 | + Border aquaBorder = UIManager.getBorder("TitledBorder.aquaVariant"); | |
98 | + if (aquaBorder != null) UIManager.put("TitledBorder.border", aquaBorder); | |
99 | + | |
100 | + // create panels | |
101 | + tabbedPane = new JTabbedPane(); | |
102 | + JPanel generalPanel = new JPanel(new MigLayout(Util.MAC_OS_X ? "insets para-10 para para para" : "")); | |
103 | + // FIXME bottom insets too large, but only in French on Leopard?! | |
104 | + JPanel mousePanel = new JPanel(new MigLayout(Util.MAC_OS_X ? String.format("insets para-10 para %s para", Util.MAC_OS_X_LEOPARD_OR_HIGHER ? "para-10" : "para") : "")); | |
105 | + JPanel tuioPanel = new JPanel(new MigLayout(Util.MAC_OS_X ? "insets para-10 para para para" : "")); | |
106 | + generalPanel.setOpaque(false); | |
107 | + mousePanel.setOpaque(false); | |
108 | + tuioPanel.setOpaque(false); | |
109 | + tabbedPane.addTab(r.getString("generalTab"), generalPanel); | |
110 | + tabbedPane.addTab(r.getString("mouseTab"), mousePanel); | |
111 | + tabbedPane.addTab(r.getString("tuioTab"), tuioPanel); | |
112 | + | |
113 | + ((JPanel)getContentPane()).setBorder(BorderFactory.createEmptyBorder(8, 10, 10, 10)); | |
114 | + add(tabbedPane, BorderLayout.CENTER); | |
115 | + | |
116 | + /* | |
117 | + * GENERAL PANEL | |
118 | + */ | |
119 | + | |
120 | + // check for updates | |
121 | + generalPanel.add(checkForUpdates = Util.newComponent(JCheckBox.class, "checkForUpdates"), "wrap"); | |
122 | + checkForUpdates.addActionListener(new ActionListener() { | |
123 | + public void actionPerformed(ActionEvent arg0) { | |
124 | + prefs.setCheckForUpdates(checkForUpdates.isSelected()); | |
125 | + } | |
126 | + }); | |
127 | + | |
128 | + // low battery warning | |
129 | + generalPanel.add(batteryWarning = Util.newComponent(JCheckBox.class, "batteryWarning"), "wrap"); | |
130 | + batteryWarning.addActionListener(new ActionListener() { | |
131 | + public void actionPerformed(ActionEvent arg0) { | |
132 | + prefs.setLowBatteryWarning(batteryWarning.isSelected()); | |
133 | + } | |
134 | + }); | |
135 | + | |
136 | + generalPanel.add(new JSeparator(), "split, span, pushx, growx, wrap"); | |
137 | + | |
138 | + generalPanel.add(Util.newComponent(JLabel.class, "language"), "split, gapbottom 3, gapleft 6"); | |
139 | + generalPanel.add(languages = Util.newComponent(JComboBox.class, "languageBox"), "wrap"); | |
140 | +// languages.putClientProperty("JComboBox.isSquare", Boolean.TRUE); | |
141 | +// languages.putClientProperty("JComboBox.isPopDown", Boolean.TRUE); | |
142 | + languages.setModel(new DefaultComboBoxModel(WWPreferences.LANGUAGES)); | |
143 | + languages.addItemListener(new ItemListener() { | |
144 | + public void itemStateChanged(ItemEvent e) { | |
145 | + if (e.getStateChange() == ItemEvent.SELECTED) { | |
146 | + prefs.setLanguage(((LocaleWrapper)languages.getSelectedItem()).getLocaleString()); | |
147 | + } | |
148 | + } | |
149 | + }); | |
150 | + generalPanel.add(Util.newComponent(JLabel.class, "languageRestartLabel"), "split, span, w 225, gapleft 6"); | |
151 | + | |
152 | +// // number of wiimotes | |
153 | +// generalPanel.add(Util.newComponent(JLabel.class, "numberOfWiimotesLabel")); | |
154 | +// ButtonGroup bg = new ButtonGroup(); | |
155 | +// for (int i = 1; i <= WWPreferences.WIIMOTES; i++) { | |
156 | +// final int j = i-1; | |
157 | +// bg.add(wiimoteNumberButtons[j] = new JRadioButton(String.valueOf(i))); | |
158 | +// wiimoteNumberButtons[j].setOpaque(false); | |
159 | +// wiimoteNumberButtons[j].addActionListener(new ActionListener() { | |
160 | +// public void actionPerformed(ActionEvent e) { | |
161 | +// prefs.setNumberOfWiimotes(j+1); | |
162 | +// } | |
163 | +// }); | |
164 | +// generalPanel.add(wiimoteNumberButtons[j], i == 1 ? "split" : ""); | |
165 | +// } | |
166 | +// generalPanel.add(Util.newComponent(JLabel.class, "numberOfWiimotesExplLabel"), "newline, split, span, center, w 290"); | |
167 | + | |
168 | + /* | |
169 | + * MOUSE CONTROL PANEL | |
170 | + */ | |
171 | + | |
172 | + // touchpad mode | |
173 | + mousePanel.add(touchpadMode = Util.newComponent(JCheckBox.class, "touchpadMode"), "wrap"); | |
174 | + touchpadMode.addActionListener(new ActionListener() { | |
175 | + public void actionPerformed(ActionEvent arg0) { | |
176 | + prefs.setCursorControl(touchpadMode.isSelected() ? TouchpadControlStrategy.class.getName() : DefaultControlStrategy.class.getName()); | |
177 | + } | |
178 | + }); | |
179 | + | |
180 | + // assist double clicks | |
181 | + mousePanel.add(assistDoubleClicks = Util.newComponent(JCheckBox.class, "assistDoubleClicks"), "wrap"); | |
182 | + assistDoubleClicks.addActionListener(new ActionListener() { | |
183 | + public void actionPerformed(ActionEvent arg0) { | |
184 | + prefs.setAssistDoubleClicks(assistDoubleClicks.isSelected()); | |
185 | + } | |
186 | + }); | |
187 | + | |
188 | + // mouse smoothing | |
189 | + final ButtonGroup smoothingGroup = new ButtonGroup(); | |
190 | + mousePanel.add(mouseSmoothing = Util.newComponent(JCheckBox.class, "mouseSmoothing"), "split"); | |
191 | + mouseSmoothing.addActionListener(new ActionListener() { | |
192 | + public void actionPerformed(ActionEvent arg0) { | |
193 | + staticSmoothing.setEnabled(mouseSmoothing.isSelected()); | |
194 | + adaptiveSmoothing.setEnabled(mouseSmoothing.isSelected()); | |
195 | + if (mouseSmoothing.isSelected()) { | |
196 | + if (smoothingGroup.getSelection() == null) | |
197 | + adaptiveSmoothing.doClick(); | |
198 | + else { | |
199 | + // trigger actionlistener | |
200 | + if (adaptiveSmoothing.isSelected()) adaptiveSmoothing.doClick(); | |
201 | + if (staticSmoothing.isSelected()) staticSmoothing.doClick(); | |
202 | + } | |
203 | + } else { | |
204 | + prefs.setMouseSmoothing(NoSmoothing.class.getName()); | |
205 | + } | |
206 | + } | |
207 | + }); | |
208 | + | |
209 | + mousePanel.add(staticSmoothing = Util.newComponent(JRadioButton.class, "staticSmoothing")); | |
210 | + staticSmoothing.addActionListener(new ActionListener() { | |
211 | + public void actionPerformed(ActionEvent e) { | |
212 | + prefs.setMouseSmoothing(SimpleMovingAverage.class.getName()); | |
213 | + } | |
214 | + }); | |
215 | + smoothingGroup.add(staticSmoothing); | |
216 | + | |
217 | + mousePanel.add(adaptiveSmoothing = Util.newComponent(JRadioButton.class, "adaptiveSmoothing")); | |
218 | + adaptiveSmoothing.addActionListener(new ActionListener() { | |
219 | + public void actionPerformed(ActionEvent e) { | |
220 | + prefs.setMouseSmoothing(AdaptiveExponentialSmoothing.class.getName()); | |
221 | + } | |
222 | + }); | |
223 | + smoothingGroup.add(adaptiveSmoothing); | |
224 | + | |
225 | + | |
226 | + | |
227 | + mousePanel.add(new JSeparator(), "newline, split, span, pushx, growx, wrap"); | |
228 | + | |
229 | + // right clicks | |
230 | + mousePanel.add(rightClicks = Util.newComponent(JCheckBox.class, "rightClicks"), "wrap"); | |
231 | + rightClicks.addActionListener(new ActionListener() { | |
232 | + public void actionPerformed(ActionEvent arg0) { | |
233 | + prefs.setRightClick(rightClicks.isSelected()); | |
234 | + } | |
235 | + }); | |
236 | + | |
237 | + // right click activation delay | |
238 | + JPanel wrapper = new JPanel(new FlowLayout(FlowLayout.CENTER, 0, 0)); | |
239 | + wrapper.setOpaque(false); | |
240 | + final TitledBorder tb = BorderFactory.createTitledBorder(null, r.getString("rightClickDelay"), TitledBorder.CENTER, TitledBorder.DEFAULT_POSITION, new JLabel().getFont().deriveFont(11f)); | |
241 | + wrapper.setBorder(tb); | |
242 | + delaySlider = Util.newComponent(JSlider.class, "delaySlider"); | |
243 | + Dictionary<Integer, JLabel> labelTable = new Hashtable<Integer, JLabel>(); | |
244 | + labelTable.put(500, new JLabel("\u00bd")); | |
245 | + labelTable.put(1000, new JLabel("1")); | |
246 | + labelTable.put(2000, new JLabel("2")); | |
247 | + labelTable.put(3000, new JLabel("3")); | |
248 | + delaySlider.setLabelTable(labelTable); | |
249 | + delaySlider.addChangeListener(new ChangeListener() { | |
250 | + public void stateChanged(ChangeEvent e) { | |
251 | + if (!delaySlider.getValueIsAdjusting() && isVisible()) { | |
252 | + prefs.setRightClickDelay(delaySlider.getValue()); | |
253 | + } | |
254 | + } | |
255 | + }); | |
256 | + Insets tbInsets = tb.getBorderInsets(wrapper); | |
257 | + int tbWidth = tb.getMinimumSize(wrapper).width-tbInsets.left-tbInsets.right+(Util.MAC_OS_X_LEOPARD_OR_HIGHER ? 0 : 20); | |
258 | + wrapper.add(delaySlider); | |
259 | + mousePanel.add(wrapper, "center, growx, w " +tbWidth); | |
260 | + | |
261 | + /* | |
262 | + * TUIO/OSC PANEL | |
263 | + */ | |
264 | + | |
265 | + // enable tuio | |
266 | + tuioPanel.add(tuioEnable = Util.newComponent(JCheckBox.class, "tuioEnable"), "wrap"); | |
267 | + tuioEnable.addActionListener(new ActionListener() { | |
268 | + public void actionPerformed(ActionEvent arg0) { | |
269 | + if (tuioEnable.isSelected()) { | |
270 | + String[] s = tuioHost.getText().split(":"); | |
271 | + String errorTitle = null; | |
272 | + if ((s.length == 1 && s[0].length() == 0) || s.length > 2) { | |
273 | + errorTitle = r.getString("tuioInvalidFormat"); | |
274 | + } else { | |
275 | + try { | |
276 | + int port = 3333; | |
277 | + if (s.length == 2) { | |
278 | + port = Integer.valueOf(s[1]); | |
279 | + if (port < 0 || port > 0xFFFF) { | |
280 | + throw new NumberFormatException(); | |
281 | + } | |
282 | + } | |
283 | + InetAddress.getByName(s[0]); | |
284 | + | |
285 | + prefs.setTuioPort(port); | |
286 | + prefs.setTuioHost(s[0]); | |
287 | + prefs.setTuioEnabled(true); | |
288 | + } catch (NumberFormatException e) { | |
289 | + errorTitle = r.getString("tuioInvalidPort", s[1]); | |
290 | + } catch (UnknownHostException e) { | |
291 | + errorTitle = r.getString("tuioInvalidHost", s[0]); | |
292 | + } | |
293 | + } | |
294 | + | |
295 | + if (errorTitle != null) { | |
296 | + JOptionPane.showMessageDialog(PreferencesWindow.this, r.getString("tuioErrorMessage"), errorTitle, JOptionPane.ERROR_MESSAGE); | |
297 | + } | |
298 | + | |
299 | + } else { | |
300 | + prefs.setTuioEnabled(false); | |
301 | + } | |
302 | + update(); | |
303 | + } | |
304 | + }); | |
305 | + | |
306 | + // tuio host | |
307 | + tuioPanel.add(tuioHost = Util.newComponent(JTextField.class, "tuioHost"), "growx, pushx, wrap"); | |
308 | + | |
309 | + | |
310 | + JPanel bottomPanel = new JPanel(new MigLayout(Util.MAC_OS_X ? "insets 0 2 0 2, gap 0" : "insets i 0 0 0, gap 0")); | |
311 | + add(bottomPanel, BorderLayout.SOUTH); | |
312 | + | |
313 | + bottomPanel.add(defaultsButton = Util.newComponent(JButton.class, "resetButton")); | |
314 | +// defaultsButton.putClientProperty("JButton.buttonType", "textured"); | |
315 | + defaultsButton.addActionListener(new ActionListener() { | |
316 | + public void actionPerformed(ActionEvent e) { | |
317 | + prefs.reset(); | |
318 | + // re-set currently chosen mouse control settings | |
319 | + // (probably not perceived as persistent preferences by user) | |
320 | + mp.update(); | |
321 | + update(); | |
322 | + } | |
323 | + }); | |
324 | + | |
325 | + JButton helpButton = new JButton(Util.getAction(hh, "help")); | |
326 | + bottomPanel.add(helpButton, "push, right"); | |
327 | + if (Util.MAC_OS_X_LEOPARD_OR_HIGHER) { | |
328 | + helpButton.setText(""); | |
329 | + helpButton.putClientProperty("JButton.buttonType", "help"); | |
330 | + } else { | |
331 | + helpButton.setText(Util.getResourceMap(HelpHandler.class).getString("help")); | |
332 | + } | |
333 | + | |
334 | + Util.getResourceMap(PreferencesWindow.class).injectComponents(this); | |
335 | + | |
336 | + pack(); | |
337 | + setResizable(false); | |
338 | + | |
339 | + if (Util.WINDOWS) { | |
340 | + addWindowFocusListener(new WindowFocusListener() { | |
341 | + public void windowGainedFocus(WindowEvent e) { | |
342 | + if (!donePack) { | |
343 | + donePack = true; | |
344 | + pack(); | |
345 | + Util.placeDialogWindow(PreferencesWindow.this, getWidth(), getHeight()); | |
346 | + } | |
347 | + } | |
348 | + public void windowLostFocus(WindowEvent e) {} | |
349 | + }); | |
350 | + } | |
351 | + } | |
352 | + | |
353 | + private void update() { | |
354 | + checkForUpdates.setSelected(prefs.checkForUpdates()); | |
355 | + batteryWarning.setSelected(prefs.isLowBatteryWarning()); | |
356 | + rightClicks.setSelected(prefs.isRightClick()); | |
357 | + assistDoubleClicks.setSelected(prefs.assistDoubleClicks()); | |
358 | + delaySlider.setValue((int)prefs.getRightClickDelay()); | |
359 | + tuioEnable.setSelected(prefs.isTuioEnabled()); | |
360 | + tuioHost.setText(String.format("%s:%d", prefs.getTuioHost(), prefs.getTuioPort())); | |
361 | + tuioHost.setEnabled(!tuioEnable.isSelected()); | |
362 | + tuioHost.setToolTipText(tuioHost.isEnabled() ? Util.getResourceMap(PreferencesWindow.class).getString("tuioHost.toolTipText") : r.getString("tuioDisableToEdit")); | |
363 | +// wiimoteNumberButtons[prefs.getNumberOfWiimotes()-1].setSelected(true); | |
364 | + | |
365 | + touchpadMode.setSelected(TouchpadControlStrategy.class.getName().equals(prefs.getCursorControl())); | |
366 | + | |
367 | + mouseSmoothing.setSelected(!NoSmoothing.class.getName().equals(prefs.getMouseSmoothing())); | |
368 | + adaptiveSmoothing.setSelected(true); | |
369 | + staticSmoothing.setSelected(SimpleMovingAverage.class.getName().equals(prefs.getMouseSmoothing())); | |
370 | + adaptiveSmoothing.setEnabled(mouseSmoothing.isSelected()); | |
371 | + staticSmoothing.setEnabled(mouseSmoothing.isSelected()); | |
372 | + | |
373 | + String lang = prefs.getLanguage(); | |
374 | + for (int i = 0; i < languages.getItemCount(); i++) { | |
375 | + LocaleWrapper lw = (LocaleWrapper)languages.getItemAt(i); | |
376 | + if (lang.equals(lw.getLocaleString())) { | |
377 | + languages.setSelectedIndex(i); | |
378 | + break; | |
379 | + } | |
380 | + } | |
381 | + } | |
382 | + | |
383 | + @Action | |
384 | + public void preferences() { | |
385 | + if (!isVisible()) { | |
386 | + update(); | |
387 | + pack(); | |
388 | +// if (Util.WINDOWS && !donePack) { | |
389 | +// setSize(getWidth(), 300); | |
390 | +// } | |
391 | + Util.placeDialogWindow(this, getWidth(), getHeight()); | |
392 | + } | |
393 | + setVisible(true); | |
394 | + } | |
395 | + | |
396 | +} | ... | ... |
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/gui/ScreenSelector.java
0 → 100644
... | ... | @@ -0,0 +1,206 @@ |
1 | +package org.mote.wiimote.whiteboard.gui; | |
2 | + | |
3 | +import java.awt.BasicStroke; | |
4 | +import java.awt.Color; | |
5 | +import java.awt.Dimension; | |
6 | +import java.awt.Graphics; | |
7 | +import java.awt.Graphics2D; | |
8 | +import java.awt.GraphicsDevice; | |
9 | +import java.awt.GraphicsEnvironment; | |
10 | +import java.awt.Point; | |
11 | +import java.awt.Rectangle; | |
12 | +import java.awt.RenderingHints; | |
13 | +import java.awt.event.ItemEvent; | |
14 | +import java.awt.event.ItemListener; | |
15 | +import java.awt.geom.Point2D; | |
16 | +import java.awt.geom.Rectangle2D; | |
17 | +import java.util.Iterator; | |
18 | +import java.util.LinkedList; | |
19 | +import java.util.List; | |
20 | +import java.util.Map; | |
21 | +import java.util.Timer; | |
22 | +import java.util.TimerTask; | |
23 | + | |
24 | +import javax.media.jai.PerspectiveTransform; | |
25 | +import javax.swing.BorderFactory; | |
26 | +import javax.swing.ButtonGroup; | |
27 | +import javax.swing.JPanel; | |
28 | +import javax.swing.JRadioButton; | |
29 | +import javax.swing.SwingConstants; | |
30 | + | |
31 | +import org.mote.wiimote.whiteboard.WiimoteDataHandler; | |
32 | +import org.mote.wiimote.whiteboard.WiimoteDataHandler.WiimoteDataListener; | |
33 | +import org.mote.wiimote.whiteboard.calibration.WiimoteCalibration; | |
34 | +import org.mote.wiimote.whiteboard.ds.IRDot; | |
35 | +import org.mote.wiimote.whiteboard.ds.Wiimote; | |
36 | +import org.mote.wiimote.whiteboard.util.Util; | |
37 | + | |
38 | +@SuppressWarnings("serial") | |
39 | +public class ScreenSelector extends JPanel implements WiimoteDataListener { | |
40 | + | |
41 | + private static final GraphicsDevice DEFAULT_SCREEN = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice(); | |
42 | + private static final int MAX_H = 130; | |
43 | + private static final int MAX_W = 185; | |
44 | + | |
45 | + private static final long REPAINT_FREQ = 1000 / 20; | |
46 | + | |
47 | + private WiimoteCalibration calibration; | |
48 | + private List<ScreenBox> screenBoxes = new LinkedList<ScreenBox>(); | |
49 | + private Point lastCursor = null, cursor = null; | |
50 | + | |
51 | + public ScreenSelector(WiimoteCalibration calibration, WiimoteDataHandler dh) { | |
52 | + super(null); | |
53 | + this.calibration = calibration; | |
54 | + dh.addWiimoteDataListener(this); | |
55 | + | |
56 | + final GraphicsDevice[] screens = GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices(); | |
57 | + | |
58 | + Rectangle r = new Rectangle(); | |
59 | + for (GraphicsDevice s : screens) { | |
60 | + r = r.union(s.getDefaultConfiguration().getBounds()); | |
61 | + } | |
62 | + | |
63 | + final int maxH = MAX_H - (screens.length == 1 ? 30 : 0); | |
64 | + int w = MAX_W; | |
65 | + int h = (int) Math.round((w * r.getHeight()) / r.getWidth()); | |
66 | + // if too high | |
67 | + if (h > maxH) { | |
68 | + h = maxH; | |
69 | + w = (int) Math.round((h * r.getWidth()) / r.getHeight()); | |
70 | + } | |
71 | + setPreferredSize(new Dimension(w, h)); | |
72 | + | |
73 | + PerspectiveTransform t = PerspectiveTransform.getQuadToQuad( | |
74 | + r.getMinX(), r.getMinY(), r.getMaxX(), r.getMinY(), | |
75 | + r.getMaxX(), r.getMaxY(), r.getMinX(), r.getMaxY(), | |
76 | + 0, 0, w, 0, w, h, 0, h); | |
77 | + | |
78 | + ButtonGroup bg = new ButtonGroup(); | |
79 | + for (int i = 0; i < screens.length; i++) { | |
80 | + Rectangle b = screens[i].getDefaultConfiguration().getBounds(); | |
81 | + final ScreenBox sb = new ScreenBox(screens[i], i, transformBounds(t, b), screens[i].equals(DEFAULT_SCREEN)); | |
82 | + bg.add(sb); | |
83 | + screenBoxes.add(sb); | |
84 | + add(sb); | |
85 | + } | |
86 | + | |
87 | + new Timer(true).schedule(new UpdateTask(), 0, REPAINT_FREQ); | |
88 | + } | |
89 | + | |
90 | + private class UpdateTask extends TimerTask { | |
91 | + @Override | |
92 | + public void run() { | |
93 | + if (cursor != lastCursor) { | |
94 | + for (ScreenBox sb : screenBoxes) { | |
95 | + if (sb.isSelected() && sb.isEnabled()) { | |
96 | + lastCursor = cursor; | |
97 | + sb.repaint(); | |
98 | + } | |
99 | + } | |
100 | + } | |
101 | + } | |
102 | + } | |
103 | + | |
104 | + private Rectangle transformBounds(PerspectiveTransform t, Rectangle b) { | |
105 | + Point2D ul = t.transform(new Point2D.Double(b.getMinX(), b.getMinY()), null); | |
106 | + Point2D ur = t.transform(new Point2D.Double(b.getMaxX(), b.getMinY()), null); | |
107 | + Point2D ll = t.transform(new Point2D.Double(b.getMinX(), b.getMaxY()), null); | |
108 | + return new Rectangle2D.Double(ul.getX(), ul.getY(), ul.distance(ur), ul.distance(ll)).getBounds(); | |
109 | + } | |
110 | + | |
111 | + @Override | |
112 | + public void setEnabled(boolean enabled) { | |
113 | + super.setEnabled(enabled); | |
114 | + enableScreenBoxes(enabled); | |
115 | + } | |
116 | + | |
117 | + private class ScreenBox extends JRadioButton { | |
118 | + private final BasicStroke STROKE = new BasicStroke(2f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER); | |
119 | + private static final int DIAMETER = 4; | |
120 | + private static final int MAX_TRACE = 1000 / (int)REPAINT_FREQ; | |
121 | + private LinkedList<Point> trace = new LinkedList<Point>(); | |
122 | + private GraphicsDevice screen; | |
123 | + private final Rectangle bounds; | |
124 | + public ScreenBox(GraphicsDevice screen, int i, Rectangle b, boolean selected) { | |
125 | + super(Util.getResourceMap(ScreenSelector.class).getString("screenText", i+1, (int)screen.getDefaultConfiguration().getBounds().getWidth(), (int)screen.getDefaultConfiguration().getBounds().getHeight())); | |
126 | + this.screen = screen; | |
127 | + bounds = screen.getDefaultConfiguration().getBounds(); | |
128 | + | |
129 | + setBounds(b); | |
130 | + setSelected(selected); | |
131 | + | |
132 | + setFocusable(false); | |
133 | + setHorizontalAlignment(SwingConstants.CENTER); | |
134 | + | |
135 | + if (!Util.MAC_OS_X) setBorder(BorderFactory.createEtchedBorder()); | |
136 | + setBorderPainted(true); | |
137 | + | |
138 | + addItemListener(new ItemListener() { | |
139 | + public void itemStateChanged(ItemEvent e) { | |
140 | + if (e.getStateChange() == ItemEvent.SELECTED) { | |
141 | + calibration.setScreen(ScreenBox.this.screen); | |
142 | + } | |
143 | + } | |
144 | + }); | |
145 | + } | |
146 | + | |
147 | + @Override | |
148 | + protected void paintComponent(Graphics g) { | |
149 | + super.paintComponent(g); | |
150 | + if (isSelected() && cursor != null) { | |
151 | + Graphics2D g2d = (Graphics2D)g; | |
152 | + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); | |
153 | + try { | |
154 | + | |
155 | + int x = Math.max(0, Math.min(getWidth(), (int)Math.round(((cursor.x - bounds.x) / bounds.getWidth()) * getWidth()))); | |
156 | + int y = Math.max(0, Math.min(getHeight(), (int)Math.round(((cursor.y - bounds.y) / bounds.getHeight()) * getHeight()))); | |
157 | + | |
158 | + if (trace.size() > MAX_TRACE) | |
159 | + trace.removeFirst(); | |
160 | + | |
161 | + if (!trace.isEmpty()) { | |
162 | + g2d.setStroke(STROKE); | |
163 | + int inc = 255/MAX_TRACE, alpha = (MAX_TRACE - trace.size()) * inc; | |
164 | + Iterator<Point> it = trace.iterator(); | |
165 | + Point lp = it.next(); | |
166 | + while (it.hasNext()) { | |
167 | + Point p = it.next(); | |
168 | + g2d.setColor(new Color(0,0,255,alpha)); | |
169 | + g2d.drawLine(lp.x, lp.y, p.x, p.y); | |
170 | + lp = p; | |
171 | + alpha += inc; | |
172 | + } | |
173 | + g2d.setColor(Color.blue); | |
174 | + g2d.drawLine(lp.x, lp.y, x, y); | |
175 | + } | |
176 | + | |
177 | + g2d.setColor(Color.blue); | |
178 | + g2d.fillOval(x-DIAMETER/2, y-DIAMETER/2, DIAMETER, DIAMETER); | |
179 | + | |
180 | + trace.addLast(new Point(x,y)); | |
181 | + } catch (NullPointerException e) { | |
182 | + trace.clear(); | |
183 | + } | |
184 | + } else { | |
185 | + trace.clear(); | |
186 | + } | |
187 | + } | |
188 | + } | |
189 | + | |
190 | + private void enableScreenBoxes(boolean enabled) { | |
191 | + | |
192 | + for (ScreenBox b : screenBoxes) | |
193 | + b.setEnabled(enabled); | |
194 | + } | |
195 | + | |
196 | + public void irWarped(Map<Wiimote, IRDot[]> data, Point[] warped) { | |
197 | + cursor = warped[0]; | |
198 | + } | |
199 | + public void batteryLevel(Wiimote wiimote, double level) {} | |
200 | + public void irLights(Wiimote wiimote, IRDot[] lights) {} | |
201 | + public void wiimoteConnected(Wiimote wiimote) {} | |
202 | + public void wiimoteDisconnected(Wiimote wiimote) {} | |
203 | + | |
204 | + | |
205 | + | |
206 | +} | ... | ... |
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/gui/ScreenSelector.java~
0 → 100644
... | ... | @@ -0,0 +1,311 @@ |
1 | +/* | |
2 | + * Copyright (C) 2008-2009, Uwe Schmidt | |
3 | + * | |
4 | + * Permission is hereby granted, free of charge, to any person obtaining a | |
5 | + * copy of this software and associated documentation files (the "Software"), | |
6 | + * to deal in the Software without restriction, including without limitation | |
7 | + * the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
8 | + * and/or sell copies of the Software, and to permit persons to whom the | |
9 | + * Software is furnished to do so, subject to the following conditions: | |
10 | + * | |
11 | + * The above copyright notice and this permission notice shall be included in | |
12 | + * all copies or substantial portions of the Software. | |
13 | + * | |
14 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
15 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
16 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
17 | + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
18 | + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |
19 | + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | |
20 | + * DEALINGS IN THE SOFTWARE. | |
21 | + * | |
22 | + * The Software uses a third-party library (WiiRemoteJ) which is not part of | |
23 | + * the Software and is subject to its own license. | |
24 | + */ | |
25 | + | |
26 | +package org.uweschmidt.wiimote.whiteboard.gui; | |
27 | + | |
28 | +import java.awt.BasicStroke; | |
29 | +import java.awt.Color; | |
30 | +import java.awt.Dimension; | |
31 | +import java.awt.Graphics; | |
32 | +import java.awt.Graphics2D; | |
33 | +import java.awt.GraphicsDevice; | |
34 | +import java.awt.GraphicsEnvironment; | |
35 | +import java.awt.Point; | |
36 | +import java.awt.Rectangle; | |
37 | +import java.awt.RenderingHints; | |
38 | +import java.awt.event.ItemEvent; | |
39 | +import java.awt.event.ItemListener; | |
40 | +import java.awt.geom.Point2D; | |
41 | +import java.awt.geom.Rectangle2D; | |
42 | +import java.util.Iterator; | |
43 | +import java.util.LinkedList; | |
44 | +import java.util.List; | |
45 | +import java.util.Map; | |
46 | +import java.util.Timer; | |
47 | +import java.util.TimerTask; | |
48 | + | |
49 | +import javax.media.jai.PerspectiveTransform; | |
50 | +import javax.swing.BorderFactory; | |
51 | +import javax.swing.ButtonGroup; | |
52 | +import javax.swing.JPanel; | |
53 | +import javax.swing.JRadioButton; | |
54 | +import javax.swing.SwingConstants; | |
55 | + | |
56 | +import org.uweschmidt.wiimote.whiteboard.WiimoteDataHandler; | |
57 | +import org.uweschmidt.wiimote.whiteboard.WiimoteDataHandler.WiimoteDataListener; | |
58 | +import org.uweschmidt.wiimote.whiteboard.calibration.WiimoteCalibration; | |
59 | +import org.uweschmidt.wiimote.whiteboard.ds.IRDot; | |
60 | +import org.uweschmidt.wiimote.whiteboard.ds.Wiimote; | |
61 | +import org.uweschmidt.wiimote.whiteboard.util.Util; | |
62 | + | |
63 | +@SuppressWarnings("serial") | |
64 | +public class ScreenSelector extends JPanel implements WiimoteDataListener { | |
65 | + | |
66 | + private static final GraphicsDevice DEFAULT_SCREEN = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice(); | |
67 | + private static final int MAX_H = 130; | |
68 | + private static final int MAX_W = 185; | |
69 | + | |
70 | + private static final long REPAINT_FREQ = 1000 / 20; | |
71 | + | |
72 | + private WiimoteCalibration calibration; | |
73 | +// private WiimoteDataHandler dh; | |
74 | + private List<ScreenBox> screenBoxes = new LinkedList<ScreenBox>(); | |
75 | + private Point lastCursor = null, cursor = null; | |
76 | + | |
77 | + public ScreenSelector(WiimoteCalibration calibration, WiimoteDataHandler dh) { | |
78 | + super(null); | |
79 | + this.calibration = calibration; | |
80 | +// this.dh = dh; | |
81 | + dh.addWiimoteDataListener(this); | |
82 | + | |
83 | + final GraphicsDevice[] screens = GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices(); | |
84 | + | |
85 | + // calculate maximum screen bounds (virtual device) | |
86 | + Rectangle r = new Rectangle(); | |
87 | + for (GraphicsDevice s : screens) { | |
88 | + r = r.union(s.getDefaultConfiguration().getBounds()); | |
89 | + } | |
90 | + | |
91 | + final int maxH = MAX_H - (screens.length == 1 ? 30 : 0); | |
92 | + // proportional bounds of panel | |
93 | + int w = MAX_W; | |
94 | + int h = (int) Math.round((w * r.getHeight()) / r.getWidth()); | |
95 | + // if too high | |
96 | + if (h > maxH) { | |
97 | + h = maxH; | |
98 | + w = (int) Math.round((h * r.getWidth()) / r.getHeight()); | |
99 | + } | |
100 | + setPreferredSize(new Dimension(w, h)); | |
101 | + | |
102 | + // map screen bounds to panel bounds | |
103 | + PerspectiveTransform t = PerspectiveTransform.getQuadToQuad( | |
104 | + r.getMinX(), r.getMinY(), r.getMaxX(), r.getMinY(), | |
105 | + r.getMaxX(), r.getMaxY(), r.getMinX(), r.getMaxY(), | |
106 | + 0, 0, w, 0, w, h, 0, h); | |
107 | + | |
108 | + // TODO temporary until multiple screens are allowed | |
109 | + ButtonGroup bg = new ButtonGroup(); | |
110 | + for (int i = 0; i < screens.length; i++) { | |
111 | + Rectangle b = screens[i].getDefaultConfiguration().getBounds(); | |
112 | + final ScreenBox sb = new ScreenBox(screens[i], i, transformBounds(t, b), screens[i].equals(DEFAULT_SCREEN)); | |
113 | + bg.add(sb); | |
114 | + screenBoxes.add(sb); | |
115 | + add(sb); | |
116 | + } | |
117 | + | |
118 | + new Timer(true).schedule(new UpdateTask(), 0, REPAINT_FREQ); | |
119 | + } | |
120 | + | |
121 | + private class UpdateTask extends TimerTask { | |
122 | + @Override | |
123 | + public void run() { | |
124 | + if (cursor != lastCursor) { | |
125 | + for (ScreenBox sb : screenBoxes) { | |
126 | + if (sb.isSelected() && sb.isEnabled()) { | |
127 | + lastCursor = cursor; | |
128 | + sb.repaint(); | |
129 | + } | |
130 | + } | |
131 | + } | |
132 | + } | |
133 | + } | |
134 | + | |
135 | + private Rectangle transformBounds(PerspectiveTransform t, Rectangle b) { | |
136 | + Point2D ul = t.transform(new Point2D.Double(b.getMinX(), b.getMinY()), null); | |
137 | + Point2D ur = t.transform(new Point2D.Double(b.getMaxX(), b.getMinY()), null); | |
138 | + Point2D ll = t.transform(new Point2D.Double(b.getMinX(), b.getMaxY()), null); | |
139 | + return new Rectangle2D.Double(ul.getX(), ul.getY(), ul.distance(ur), ul.distance(ll)).getBounds(); | |
140 | + } | |
141 | + | |
142 | + @Override | |
143 | + public void setEnabled(boolean enabled) { | |
144 | + super.setEnabled(enabled); | |
145 | + enableScreenBoxes(enabled); | |
146 | + } | |
147 | + | |
148 | + // TODO use JToggleButton instead of JRadioButton or JCheckBox | |
149 | + private class ScreenBox extends JRadioButton { | |
150 | + private final BasicStroke STROKE = new BasicStroke(2f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER); | |
151 | + private static final int DIAMETER = 4; | |
152 | + private static final int MAX_TRACE = 1000 / (int)REPAINT_FREQ; | |
153 | + private LinkedList<Point> trace = new LinkedList<Point>(); | |
154 | + private GraphicsDevice screen; | |
155 | + private final Rectangle bounds; | |
156 | +// private final int screenWidth, screenHeight; | |
157 | + public ScreenBox(GraphicsDevice screen, int i, Rectangle b, boolean selected) { | |
158 | + super(Util.getResourceMap(ScreenSelector.class).getString("screenText", i+1, (int)screen.getDefaultConfiguration().getBounds().getWidth(), (int)screen.getDefaultConfiguration().getBounds().getHeight())); | |
159 | + this.screen = screen; | |
160 | + bounds = screen.getDefaultConfiguration().getBounds(); | |
161 | +// screenWidth = (int)screen.getDefaultConfiguration().getBounds().getWidth(); | |
162 | +// screenHeight = (int)screen.getDefaultConfiguration().getBounds().getHeight(); | |
163 | + | |
164 | + setBounds(b); | |
165 | + setSelected(selected); | |
166 | + | |
167 | + setFocusable(false); | |
168 | + setHorizontalAlignment(SwingConstants.CENTER); | |
169 | + | |
170 | + if (!Util.MAC_OS_X) setBorder(BorderFactory.createEtchedBorder()); | |
171 | + setBorderPainted(true); | |
172 | + | |
173 | + addItemListener(new ItemListener() { | |
174 | + public void itemStateChanged(ItemEvent e) { | |
175 | + if (e.getStateChange() == ItemEvent.SELECTED) { | |
176 | + calibration.setScreen(ScreenBox.this.screen); | |
177 | + } | |
178 | +// enableScreenBoxes(true); | |
179 | + } | |
180 | + }); | |
181 | + } | |
182 | + | |
183 | + @Override | |
184 | + protected void paintComponent(Graphics g) { | |
185 | + super.paintComponent(g); | |
186 | + if (isSelected() && cursor != null) { | |
187 | + Graphics2D g2d = (Graphics2D)g; | |
188 | + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); | |
189 | + try { | |
190 | +// g.setColor(new Color(255,255,255,75)); | |
191 | +// g.fillRect(0, 0, getWidth(), getHeight()); | |
192 | + | |
193 | + int x = Math.max(0, Math.min(getWidth(), (int)Math.round(((cursor.x - bounds.x) / bounds.getWidth()) * getWidth()))); | |
194 | + int y = Math.max(0, Math.min(getHeight(), (int)Math.round(((cursor.y - bounds.y) / bounds.getHeight()) * getHeight()))); | |
195 | + | |
196 | + if (trace.size() > MAX_TRACE) | |
197 | + trace.removeFirst(); | |
198 | + | |
199 | + if (!trace.isEmpty()) { | |
200 | + g2d.setStroke(STROKE); | |
201 | + int inc = 255/MAX_TRACE, alpha = (MAX_TRACE - trace.size()) * inc; | |
202 | + Iterator<Point> it = trace.iterator(); | |
203 | + Point lp = it.next(); | |
204 | + while (it.hasNext()) { | |
205 | + Point p = it.next(); | |
206 | + g2d.setColor(new Color(0,0,255,alpha)); | |
207 | + g2d.drawLine(lp.x, lp.y, p.x, p.y); | |
208 | + lp = p; | |
209 | + alpha += inc; | |
210 | + } | |
211 | + g2d.setColor(Color.blue); | |
212 | + g2d.drawLine(lp.x, lp.y, x, y); | |
213 | + } | |
214 | + | |
215 | + g2d.setColor(Color.blue); | |
216 | + g2d.fillOval(x-DIAMETER/2, y-DIAMETER/2, DIAMETER, DIAMETER); | |
217 | + | |
218 | + trace.addLast(new Point(x,y)); | |
219 | + } catch (NullPointerException e) { | |
220 | + // cursor can be null because of UpdateTask, no problem | |
221 | + trace.clear(); | |
222 | + } | |
223 | + } else { | |
224 | + trace.clear(); | |
225 | + } | |
226 | + } | |
227 | + } | |
228 | + | |
229 | + private void enableScreenBoxes(boolean enabled) { | |
230 | + | |
231 | + for (ScreenBox b : screenBoxes) | |
232 | + b.setEnabled(enabled); | |
233 | + | |
234 | +// if (!enabled) { | |
235 | +// for (ScreenBox b : screenBoxes) | |
236 | +// b.setEnabled(false); | |
237 | +// } else { | |
238 | +// LinkedList<ScreenBox> list = new LinkedList<ScreenBox>(); | |
239 | +// | |
240 | +// // enable all screens | |
241 | +// for (ScreenBox sb : screenBoxes) { | |
242 | +// sb.setEnabled(true); | |
243 | +// if (sb.isSelected()) list.add(sb); | |
244 | +// } | |
245 | +// | |
246 | +// // disallow deselection if only screen left | |
247 | +// if (list.size() == 1) | |
248 | +// list.getFirst().setEnabled(false); | |
249 | +// } | |
250 | + } | |
251 | + | |
252 | + public void irWarped(Map<Wiimote, IRDot[]> data, Point[] warped) { | |
253 | + cursor = warped[0]; | |
254 | + } | |
255 | + public void batteryLevel(Wiimote wiimote, double level) {} | |
256 | + public void irLights(Wiimote wiimote, IRDot[] lights) {} | |
257 | + public void wiimoteConnected(Wiimote wiimote) {} | |
258 | + public void wiimoteDisconnected(Wiimote wiimote) {} | |
259 | + | |
260 | + | |
261 | +// private class ScreenLabel extends JLabel { | |
262 | +// | |
263 | +// private boolean selected; | |
264 | +// private GraphicsDevice screen; | |
265 | +// | |
266 | +// public ScreenLabel(GraphicsDevice screen, int i, Rectangle b, boolean selected) { | |
267 | +// super(String.format("<html><center>Screen %d<br><small>%d x %d</small></center></html>", i+1, (int)screen.getDefaultConfiguration().getBounds().getWidth(), (int)screen.getDefaultConfiguration().getBounds().getHeight())); | |
268 | +// this.screen = screen; | |
269 | +// setSelected(selected); | |
270 | +// setBounds(b); | |
271 | +// setOpaque(true); | |
272 | +// setBorder(BorderFactory.createLineBorder(SystemColor.windowBorder)); | |
273 | +// setHorizontalAlignment(SwingConstants.CENTER); | |
274 | +// | |
275 | +// final ScreenLabel thisLabel = this; | |
276 | +// addMouseListener(new MouseAdapter() { | |
277 | +// @Override | |
278 | +// public void mousePressed(MouseEvent e) { | |
279 | +// if (thisLabel.isEnabled() && !thisLabel.isSelected()) { | |
280 | +// calibration.setScreen(thisLabel.screen); | |
281 | +// thisLabel.setSelected(true); | |
282 | +// repaint(); | |
283 | +// for (ScreenLabel l : screenLabels) { | |
284 | +// if (thisLabel != l) { | |
285 | +// l.setSelected(false); | |
286 | +// l.repaint(); | |
287 | +// } | |
288 | +// } | |
289 | +// } | |
290 | +// } | |
291 | +// }); | |
292 | +// } | |
293 | +// | |
294 | +// @Override | |
295 | +// public void setEnabled(boolean enabled) { | |
296 | +// super.setEnabled(enabled); | |
297 | +// setForeground(enabled ? SystemColor.textText : SystemColor.textInactiveText); | |
298 | +// } | |
299 | +// | |
300 | +// public void setSelected(boolean selected) { | |
301 | +// this.selected = selected; | |
302 | +// setBackground(selected ? SystemColor.textHighlight : SystemColor.window); | |
303 | +// } | |
304 | +// | |
305 | +// public boolean isSelected() { | |
306 | +// return selected; | |
307 | +// } | |
308 | +// | |
309 | +// } | |
310 | + | |
311 | +} | ... | ... |
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/gui/WarpedMonitor.java
0 → 100644
... | ... | @@ -0,0 +1,137 @@ |
1 | +package org.mote.wiimote.whiteboard.gui; | |
2 | + | |
3 | +import java.awt.BorderLayout; | |
4 | +import java.awt.Color; | |
5 | +import java.awt.GraphicsDevice; | |
6 | +import java.awt.Point; | |
7 | +import java.awt.Rectangle; | |
8 | +import java.awt.event.KeyAdapter; | |
9 | +import java.awt.event.KeyEvent; | |
10 | +import java.awt.geom.Point2D; | |
11 | +import java.util.Map; | |
12 | +import java.util.Timer; | |
13 | +import java.util.TimerTask; | |
14 | + | |
15 | +import javax.swing.BorderFactory; | |
16 | +import javax.swing.JDialog; | |
17 | +import javax.swing.JPanel; | |
18 | + | |
19 | +import org.jdesktop.application.Action; | |
20 | +import org.jdesktop.application.Application; | |
21 | +import org.mote.wiimote.whiteboard.WiimoteDataHandler; | |
22 | +import org.mote.wiimote.whiteboard.WiimoteWhiteboard; | |
23 | +import org.mote.wiimote.whiteboard.WiimoteDataHandler.WiimoteDataListener; | |
24 | +import org.mote.wiimote.whiteboard.calibration.WiimoteCalibration; | |
25 | +import org.mote.wiimote.whiteboard.ds.IRDot; | |
26 | +import org.mote.wiimote.whiteboard.ds.Wiimote; | |
27 | +import org.mote.wiimote.whiteboard.preferences.WWPreferences; | |
28 | +import org.mote.wiimote.whiteboard.util.Util; | |
29 | + | |
30 | +@SuppressWarnings("serial") | |
31 | +public class WarpedMonitor extends JDialog implements WiimoteDataListener { | |
32 | + | |
33 | + private static final int REPAINT_FREQ = 1000 / 50; | |
34 | + static final int RADIUS = 10; | |
35 | + | |
36 | + private JPanel canvas; | |
37 | + private Point2D[][] lights = new Point2D[WWPreferences.WIIMOTES+1][4]; | |
38 | + private LightLabel[][] labels = new LightLabel[WWPreferences.WIIMOTES+1][4]; | |
39 | + private WiimoteCalibration calibration; | |
40 | + private WiimoteDataHandler dh; | |
41 | + | |
42 | + public WarpedMonitor(WiimoteDataHandler dh, final WiimoteCalibration calibration) { | |
43 | + super(Application.getInstance(WiimoteWhiteboard.class).getMainFrame(), "Warped Point Monitor"); | |
44 | + setLayout(new BorderLayout()); | |
45 | + dh.addWiimoteDataListener(this); | |
46 | + this.calibration = calibration; | |
47 | + this.dh = dh; | |
48 | + ((JPanel) getContentPane()).setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); | |
49 | + | |
50 | + canvas = new JPanel(null, true); | |
51 | + canvas.setBorder(BorderFactory.createLineBorder(Color.black)); | |
52 | + add(canvas, BorderLayout.CENTER); | |
53 | + | |
54 | + setUndecorated(true); | |
55 | + addKeyListener(new KeyAdapter() { | |
56 | + @Override | |
57 | + public void keyPressed(KeyEvent e) { | |
58 | + if (e.getKeyCode() == KeyEvent.VK_F || e.getKeyCode() == KeyEvent.VK_ESCAPE) { | |
59 | + GraphicsDevice screen = calibration.getScreen(); | |
60 | + if (screen != null) { | |
61 | + if (screen.getFullScreenWindow() == WarpedMonitor.this) { | |
62 | + ((JPanel) getContentPane()).setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); | |
63 | + screen.setFullScreenWindow(null); | |
64 | + Util.placeDialogWindow(WarpedMonitor.this, 640, 480); | |
65 | + } else { | |
66 | + if (e.getKeyCode() == KeyEvent.VK_ESCAPE) | |
67 | + WarpedMonitor.this.setVisible(false); | |
68 | + else { | |
69 | + ((JPanel) getContentPane()).setBorder(BorderFactory.createEmptyBorder()); | |
70 | + screen.setFullScreenWindow(WarpedMonitor.this); | |
71 | + } | |
72 | + } | |
73 | + } | |
74 | + } | |
75 | + } | |
76 | + }); | |
77 | + | |
78 | + Util.placeDialogWindow(this, 640, 480); | |
79 | + new Timer(true).schedule(new UpdateTask(), 0, REPAINT_FREQ); | |
80 | + } | |
81 | + | |
82 | + @Action | |
83 | + public void monitor() { | |
84 | + setVisible(true); | |
85 | + } | |
86 | + | |
87 | + private class UpdateTask extends TimerTask { | |
88 | + @Override | |
89 | + public void run() { | |
90 | + if (isVisible()) { | |
91 | + for (int i = WWPreferences.WIIMOTES; i >= 0; i--) | |
92 | + for (int j = 0; j < 4; j++) { | |
93 | + if (labels[i][j] != null) | |
94 | + labels[i][j].update(); | |
95 | + } | |
96 | + } | |
97 | + } | |
98 | + } | |
99 | + | |
100 | + public void irLights(Wiimote wiimote, IRDot[] lights) { | |
101 | + } | |
102 | + | |
103 | + public void irWarped(Map<Wiimote, IRDot[]> data, Point[] warped) { | |
104 | + if (isVisible()) { | |
105 | + Rectangle bounds = calibration.getScreen().getDefaultConfiguration().getBounds(); | |
106 | + for (int i = 0; i < 4; i++) { | |
107 | + final Point w = warped[i]; | |
108 | + this.lights[0][i] = w == null ? null : new Point2D.Double(w.getX() / bounds.getWidth(), 1 - w.getY() / bounds.getHeight()); | |
109 | + } | |
110 | + for (Wiimote wiimote : dh.getConnectedWiimotes()) { | |
111 | + for (int i = 0; i < 4; i++) { | |
112 | + final Point2D w = calibration.warp(i, wiimote, data); | |
113 | + this.lights[wiimote.getId()][i] = w == null ? null : new Point2D.Double(w.getX() / bounds.getWidth(), 1 - w.getY() / bounds.getHeight()); | |
114 | + } | |
115 | + } | |
116 | + } | |
117 | + } | |
118 | + | |
119 | + public void batteryLevel(Wiimote wiimote, double level) { | |
120 | + } | |
121 | + | |
122 | + public void wiimoteConnected(Wiimote wiimote) { | |
123 | + if (wiimote.getId() == 1) | |
124 | + for (int i = 0; i < 4; i++) | |
125 | + canvas.add(labels[0][i] = new LightLabel(canvas, lights, 0, i + 1, Color.red)); | |
126 | + for (int i = 0; i < 4; i++) | |
127 | + canvas.add(labels[wiimote.getId()][i] = new LightLabel(canvas, lights, wiimote.getId(), i + 1)); | |
128 | + } | |
129 | + | |
130 | + public void wiimoteDisconnected(Wiimote wiimote) { | |
131 | + for (int i = 0; i < 4; i++) { | |
132 | + canvas.remove(labels[wiimote.getId()][i]); | |
133 | + labels[wiimote.getId()][i] = null; | |
134 | + } | |
135 | + } | |
136 | + | |
137 | +} | ... | ... |
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/gui/WarpedMonitor.java~
0 → 100644
... | ... | @@ -0,0 +1,183 @@ |
1 | +/* | |
2 | + * Copyright (C) 2008-2009, Uwe Schmidt | |
3 | + * | |
4 | + * Permission is hereby granted, free of charge, to any person obtaining a | |
5 | + * copy of this software and associated documentation files (the "Software"), | |
6 | + * to deal in the Software without restriction, including without limitation | |
7 | + * the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
8 | + * and/or sell copies of the Software, and to permit persons to whom the | |
9 | + * Software is furnished to do so, subject to the following conditions: | |
10 | + * | |
11 | + * The above copyright notice and this permission notice shall be included in | |
12 | + * all copies or substantial portions of the Software. | |
13 | + * | |
14 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
15 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
16 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
17 | + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
18 | + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |
19 | + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | |
20 | + * DEALINGS IN THE SOFTWARE. | |
21 | + * | |
22 | + * The Software uses a third-party library (WiiRemoteJ) which is not part of | |
23 | + * the Software and is subject to its own license. | |
24 | + */ | |
25 | + | |
26 | +package org.uweschmidt.wiimote.whiteboard.gui; | |
27 | + | |
28 | +import java.awt.BorderLayout; | |
29 | +import java.awt.Color; | |
30 | +import java.awt.GraphicsDevice; | |
31 | +import java.awt.Point; | |
32 | +import java.awt.Rectangle; | |
33 | +import java.awt.event.KeyAdapter; | |
34 | +import java.awt.event.KeyEvent; | |
35 | +import java.awt.geom.Point2D; | |
36 | +import java.util.Map; | |
37 | +import java.util.Timer; | |
38 | +import java.util.TimerTask; | |
39 | + | |
40 | +import javax.swing.BorderFactory; | |
41 | +import javax.swing.JDialog; | |
42 | +import javax.swing.JPanel; | |
43 | + | |
44 | +import org.jdesktop.application.Action; | |
45 | +import org.jdesktop.application.Application; | |
46 | +import org.uweschmidt.wiimote.whiteboard.WiimoteDataHandler; | |
47 | +import org.uweschmidt.wiimote.whiteboard.WiimoteWhiteboard; | |
48 | +import org.uweschmidt.wiimote.whiteboard.WiimoteDataHandler.WiimoteDataListener; | |
49 | +import org.uweschmidt.wiimote.whiteboard.calibration.WiimoteCalibration; | |
50 | +import org.uweschmidt.wiimote.whiteboard.ds.IRDot; | |
51 | +import org.uweschmidt.wiimote.whiteboard.ds.Wiimote; | |
52 | +import org.uweschmidt.wiimote.whiteboard.preferences.WWPreferences; | |
53 | +import org.uweschmidt.wiimote.whiteboard.util.Util; | |
54 | + | |
55 | +@SuppressWarnings("serial") | |
56 | +public class WarpedMonitor extends JDialog implements WiimoteDataListener { | |
57 | + | |
58 | + private static final int REPAINT_FREQ = 1000 / 50; | |
59 | + static final int RADIUS = 10; | |
60 | + | |
61 | + private JPanel canvas; | |
62 | + private Point2D[][] lights = new Point2D[WWPreferences.WIIMOTES+1][4]; | |
63 | + private LightLabel[][] labels = new LightLabel[WWPreferences.WIIMOTES+1][4]; | |
64 | + private WiimoteCalibration calibration; | |
65 | + private WiimoteDataHandler dh; | |
66 | + | |
67 | + public WarpedMonitor(WiimoteDataHandler dh, final WiimoteCalibration calibration) { | |
68 | + super(Application.getInstance(WiimoteWhiteboard.class).getMainFrame(), "Warped Point Monitor"); | |
69 | + setLayout(new BorderLayout()); | |
70 | + dh.addWiimoteDataListener(this); | |
71 | + this.calibration = calibration; | |
72 | + this.dh = dh; | |
73 | + ((JPanel) getContentPane()).setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); | |
74 | + | |
75 | + canvas = new JPanel(null, true); | |
76 | + canvas.setBorder(BorderFactory.createLineBorder(Color.black)); | |
77 | + add(canvas, BorderLayout.CENTER); | |
78 | + | |
79 | + setUndecorated(true); | |
80 | + addKeyListener(new KeyAdapter() { | |
81 | + @Override | |
82 | + public void keyPressed(KeyEvent e) { | |
83 | + if (e.getKeyCode() == KeyEvent.VK_F || e.getKeyCode() == KeyEvent.VK_ESCAPE) { | |
84 | + GraphicsDevice screen = calibration.getScreen(); | |
85 | + if (screen != null) { | |
86 | + if (screen.getFullScreenWindow() == WarpedMonitor.this) { | |
87 | + ((JPanel) getContentPane()).setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); | |
88 | + screen.setFullScreenWindow(null); | |
89 | + Util.placeDialogWindow(WarpedMonitor.this, 640, 480); | |
90 | + } else { | |
91 | + if (e.getKeyCode() == KeyEvent.VK_ESCAPE) | |
92 | + WarpedMonitor.this.setVisible(false); | |
93 | + else { | |
94 | + ((JPanel) getContentPane()).setBorder(BorderFactory.createEmptyBorder()); | |
95 | + screen.setFullScreenWindow(WarpedMonitor.this); | |
96 | + } | |
97 | + } | |
98 | + } | |
99 | + } | |
100 | + } | |
101 | + }); | |
102 | + | |
103 | + Util.placeDialogWindow(this, 640, 480); | |
104 | + new Timer(true).schedule(new UpdateTask(), 0, REPAINT_FREQ); | |
105 | + } | |
106 | + | |
107 | + @Action | |
108 | + public void monitor() { | |
109 | + setVisible(true); | |
110 | + } | |
111 | + | |
112 | + private class UpdateTask extends TimerTask { | |
113 | + @Override | |
114 | + public void run() { | |
115 | + if (isVisible()) { | |
116 | + for (int i = WWPreferences.WIIMOTES; i >= 0; i--) | |
117 | + for (int j = 0; j < 4; j++) { | |
118 | + if (labels[i][j] != null) | |
119 | + labels[i][j].update(); | |
120 | + } | |
121 | + } | |
122 | + } | |
123 | + } | |
124 | + | |
125 | + public void irLights(Wiimote wiimote, IRDot[] lights) { | |
126 | + } | |
127 | + | |
128 | + public void irWarped(Map<Wiimote, IRDot[]> data, Point[] warped) { | |
129 | + if (isVisible()) { | |
130 | + Rectangle bounds = calibration.getScreen().getDefaultConfiguration().getBounds(); | |
131 | + for (int i = 0; i < 4; i++) { | |
132 | + final Point w = warped[i]; | |
133 | + this.lights[0][i] = w == null ? null : new Point2D.Double(w.getX() / bounds.getWidth(), 1 - w.getY() / bounds.getHeight()); | |
134 | + } | |
135 | +// Map<String, Point[]> warpedData = new LinkedHashMap<String, Point[]>(); | |
136 | + for (Wiimote wiimote : dh.getConnectedWiimotes()) { | |
137 | +// Point[] pArr = new Point[4]; | |
138 | + for (int i = 0; i < 4; i++) { | |
139 | + final Point2D w = calibration.warp(i, wiimote, data); | |
140 | +// pArr[i] = w; | |
141 | + this.lights[wiimote.getId()][i] = w == null ? null : new Point2D.Double(w.getX() / bounds.getWidth(), 1 - w.getY() / bounds.getHeight()); | |
142 | + } | |
143 | +// warpedData.put(address, pArr); | |
144 | + } | |
145 | +// | |
146 | +// Point[][] cluster = PointClusterer.cluster(warpedData); | |
147 | +// for (int i = 0; i < cluster.length; i++) { | |
148 | +// Point[] c = cluster[i]; | |
149 | +// double x = 0, y = 0; | |
150 | +// for (Point d : c) { | |
151 | +// x += d.getX(); | |
152 | +// y += d.getY(); | |
153 | +// } | |
154 | +// final Point2D w = new Point2D.Double(x/c.length, y/c.length); | |
155 | +// this.lights[0][i] = new Point2D.Double(w.getX() / bounds.getWidth(), 1 - w.getY() / bounds.getHeight()); | |
156 | +//// System.out.println(this.lights[0][i]); | |
157 | +// } | |
158 | +// for (int i = cluster.length; i < 4; i++) | |
159 | +// this.lights[0][i] = null; | |
160 | + | |
161 | + | |
162 | + } | |
163 | + } | |
164 | + | |
165 | + public void batteryLevel(Wiimote wiimote, double level) { | |
166 | + } | |
167 | + | |
168 | + public void wiimoteConnected(Wiimote wiimote) { | |
169 | + if (wiimote.getId() == 1) | |
170 | + for (int i = 0; i < 4; i++) | |
171 | + canvas.add(labels[0][i] = new LightLabel(canvas, lights, 0, i + 1, Color.red)); | |
172 | + for (int i = 0; i < 4; i++) | |
173 | + canvas.add(labels[wiimote.getId()][i] = new LightLabel(canvas, lights, wiimote.getId(), i + 1)); | |
174 | + } | |
175 | + | |
176 | + public void wiimoteDisconnected(Wiimote wiimote) { | |
177 | + for (int i = 0; i < 4; i++) { | |
178 | + canvas.remove(labels[wiimote.getId()][i]); | |
179 | + labels[wiimote.getId()][i] = null; | |
180 | + } | |
181 | + } | |
182 | + | |
183 | +} | ... | ... |
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/gui/WiimoteIcon.java
0 → 100644
... | ... | @@ -0,0 +1,35 @@ |
1 | +package org.mote.wiimote.whiteboard.gui; | |
2 | + | |
3 | +import java.awt.Color; | |
4 | +import java.awt.SystemColor; | |
5 | + | |
6 | +import javax.swing.BorderFactory; | |
7 | +import javax.swing.JLabel; | |
8 | +import javax.swing.JPanel; | |
9 | + | |
10 | +import net.miginfocom.swing.MigLayout; | |
11 | + | |
12 | +@SuppressWarnings("serial") | |
13 | +public class WiimoteIcon extends JPanel { | |
14 | + | |
15 | + public static final Color[] COLORS = { Color.white, Color.pink, Color.orange, Color.green }; | |
16 | + private JLabel idLabel; | |
17 | + | |
18 | + public WiimoteIcon(int id) { | |
19 | + setBackground(COLORS[id - 1]); | |
20 | + setBorder(BorderFactory.createLineBorder(Color.lightGray)); | |
21 | + setLayout(new MigLayout("insets 3, center")); | |
22 | + for (int i = 1; i <= 4; i++) { | |
23 | + JLabel l = new JLabel(); | |
24 | + l.setOpaque(true); | |
25 | + l.setBackground(SystemColor.textInactiveText); | |
26 | + add(l, "w 6!, h 6!"); | |
27 | + if (i == id) idLabel = l; | |
28 | + } | |
29 | + } | |
30 | + | |
31 | + public void displayConnected(boolean connected) { | |
32 | + idLabel.setBackground(connected ? Color.blue : SystemColor.textInactiveText); | |
33 | + } | |
34 | + | |
35 | +} | ... | ... |
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/gui/WiimoteIcon.java~
0 → 100644
... | ... | @@ -0,0 +1,60 @@ |
1 | +/* | |
2 | + * Copyright (C) 2008-2009, Uwe Schmidt | |
3 | + * | |
4 | + * Permission is hereby granted, free of charge, to any person obtaining a | |
5 | + * copy of this software and associated documentation files (the "Software"), | |
6 | + * to deal in the Software without restriction, including without limitation | |
7 | + * the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
8 | + * and/or sell copies of the Software, and to permit persons to whom the | |
9 | + * Software is furnished to do so, subject to the following conditions: | |
10 | + * | |
11 | + * The above copyright notice and this permission notice shall be included in | |
12 | + * all copies or substantial portions of the Software. | |
13 | + * | |
14 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
15 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
16 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
17 | + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
18 | + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |
19 | + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | |
20 | + * DEALINGS IN THE SOFTWARE. | |
21 | + * | |
22 | + * The Software uses a third-party library (WiiRemoteJ) which is not part of | |
23 | + * the Software and is subject to its own license. | |
24 | + */ | |
25 | + | |
26 | +package org.uweschmidt.wiimote.whiteboard.gui; | |
27 | + | |
28 | +import java.awt.Color; | |
29 | +import java.awt.SystemColor; | |
30 | + | |
31 | +import javax.swing.BorderFactory; | |
32 | +import javax.swing.JLabel; | |
33 | +import javax.swing.JPanel; | |
34 | + | |
35 | +import net.miginfocom.swing.MigLayout; | |
36 | + | |
37 | +@SuppressWarnings("serial") | |
38 | +public class WiimoteIcon extends JPanel { | |
39 | + | |
40 | + public static final Color[] COLORS = { Color.white, Color.pink, Color.orange, Color.green }; | |
41 | + private JLabel idLabel; | |
42 | + | |
43 | + public WiimoteIcon(int id) { | |
44 | + setBackground(COLORS[id - 1]); | |
45 | + setBorder(BorderFactory.createLineBorder(Color.lightGray)); | |
46 | + setLayout(new MigLayout("insets 3, center")); | |
47 | + for (int i = 1; i <= 4; i++) { | |
48 | + JLabel l = new JLabel(); | |
49 | + l.setOpaque(true); | |
50 | + l.setBackground(SystemColor.textInactiveText); | |
51 | + add(l, "w 6!, h 6!"); | |
52 | + if (i == id) idLabel = l; | |
53 | + } | |
54 | + } | |
55 | + | |
56 | + public void displayConnected(boolean connected) { | |
57 | + idLabel.setBackground(connected ? Color.blue : SystemColor.textInactiveText); | |
58 | + } | |
59 | + | |
60 | +} | ... | ... |
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/gui/resources/AboutWindow.properties
0 → 100644
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/gui/resources/AboutWindow_de.properties
0 → 100644
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/gui/resources/AboutWindow_es.properties
0 → 100644
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/gui/resources/AboutWindow_et.properties
0 → 100644
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/gui/resources/AboutWindow_fr.properties
0 → 100644
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/gui/resources/AboutWindow_in.properties
0 → 100644
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/gui/resources/AboutWindow_it.properties
0 → 100644
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/gui/resources/AboutWindow_pl.properties
0 → 100644
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/gui/resources/AboutWindow_pt.properties
0 → 100644
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/gui/resources/AboutWindow_ru.properties
0 → 100644
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/gui/resources/AboutWindow_ru_bak.properties
0 → 100644
... | ... | @@ -0,0 +1,4 @@ |
1 | +#Translated by Dmitry Pupinin (dlnsk@mail.ru) | |
2 | +#Mon Jan 25 00:31:50 NOVT 2010 | |
3 | +about.Action.text=\u041e \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0435 | |
4 | +copyRight=\u0410\u0432\u0442\u043e\u0440\u0441\u043a\u043e\u0435 \u043f\u0440\u0430\u0432\u043e \u00a9 %s %s | ... | ... |
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/gui/resources/AboutWindow_sl.properties
0 → 100644
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/gui/resources/CalibrationInfoWindow.properties
0 → 100644
... | ... | @@ -0,0 +1,24 @@ |
1 | +info.Action.text = Calibration Details | |
2 | +info.Action.shortDescription = Shows details about the current calibration settings. | |
3 | + | |
4 | +# no | |
5 | +trackingUtility.minimum = 0 | |
6 | +# no | |
7 | +trackingUtility.maximum = 503316 | |
8 | +# no | |
9 | +trackingUtility.stringPainted = true | |
10 | +# no | |
11 | +trackingUtility.toolTipText = | |
12 | + | |
13 | +# no | |
14 | +trackingAreaLabel.horizontalAlignment = 0 | |
15 | +# no | |
16 | +calibratedScreenLabel.horizontalAlignment = 0 | |
17 | +# no | |
18 | +trackingAreaLabel.opaque = true | |
19 | +# no | |
20 | +calibratedScreenLabel.opaque = true | |
21 | + | |
22 | +trackingAreaLabel.text = Wiimote Tracking Area | |
23 | +calibratedScreenLabel.text = Calibrated Screen Area | |
24 | +trackingUtilString = Tracking Utilization: %.0f%% | ... | ... |
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/gui/resources/CalibrationInfoWindow_de.properties
0 → 100644
... | ... | @@ -0,0 +1,12 @@ |
1 | +#:1223760565,Uwe | |
2 | + | |
3 | +info.Action.text=Kalibrierungs-Details | |
4 | + | |
5 | +info.Action.shortDescription=Zeigt Details \u00fcber die aktuelle Kalibrierung. | |
6 | + | |
7 | +trackingAreaLabel.text=Tracking-Bereich der Wiimote | |
8 | + | |
9 | +calibratedScreenLabel.text=Kalibrierter Bildschirm-Bereich | |
10 | + | |
11 | +trackingUtilString=Tracking-Nutzung: %.0f%% | |
12 | + | ... | ... |
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/gui/resources/CalibrationInfoWindow_es.properties
0 → 100644
... | ... | @@ -0,0 +1,12 @@ |
1 | +#:1224611921,Descalzo | |
2 | + | |
3 | +info.Action.text=Detalles de Calibraci\u00f3n | |
4 | + | |
5 | +info.Action.shortDescription=Las detalles de las posici\u00f3nes de la calibraci\u00f3n corriente. | |
6 | + | |
7 | +trackingAreaLabel.text=\u00c1rea de Rastreo del Wiimote | |
8 | + | |
9 | +calibratedScreenLabel.text=\u00c1rea de la Pantalla Calibrada | |
10 | + | |
11 | +trackingUtilString=Utilizaci\u00f3n de Rastreo: %.0f%% | |
12 | + | ... | ... |
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/gui/resources/CalibrationInfoWindow_et.properties
0 → 100644
... | ... | @@ -0,0 +1,12 @@ |
1 | +#:1248457890,Marko | |
2 | + | |
3 | +info.Action.text=Kalibreerimise \u00fcksikasjad | |
4 | + | |
5 | +info.Action.shortDescription=N\u00e4itab hetkel kehtiva kalibreeringu \u00fcksikasju. | |
6 | + | |
7 | +trackingAreaLabel.text=Wii puldi poolt j\u00e4lgitav ala | |
8 | + | |
9 | +calibratedScreenLabel.text=Kalibreeritud ekraani ala | |
10 | + | |
11 | +trackingUtilString=J\u00e4lgitava ala h\u00f5ivatus: %.0f%% | |
12 | + | ... | ... |
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/gui/resources/CalibrationInfoWindow_fr.properties
0 → 100644
... | ... | @@ -0,0 +1,12 @@ |
1 | +#:1223892916,Francois | |
2 | + | |
3 | +info.Action.text=D\u00e9tails de la calibration | |
4 | + | |
5 | +info.Action.shortDescription=Pr\u00e9sente en d\u00e9tail les param\u00e8tres de la calibration en cours. | |
6 | + | |
7 | +trackingAreaLabel.text=Aire suivie par la Wiimote | |
8 | + | |
9 | +calibratedScreenLabel.text=Aire d'\u00e9cran calibr\u00e9 | |
10 | + | |
11 | +trackingUtilString=Utilisation du suivi: %.0f%% | |
12 | + | ... | ... |
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/gui/resources/CalibrationInfoWindow_in.properties
0 → 100644
... | ... | @@ -0,0 +1,12 @@ |
1 | +#:1274618605,Bobby | |
2 | + | |
3 | +info.Action.text=Detail Kalibrasi | |
4 | + | |
5 | +info.Action.shortDescription=Menunjukkan informasi detail mengenai setting Kalibrasi yang digunakan. | |
6 | + | |
7 | +trackingAreaLabel.text=Area Tracking Wiimote | |
8 | + | |
9 | +calibratedScreenLabel.text=Area layar yang terkalibrasi | |
10 | + | |
11 | +trackingUtilString=Penggunaan Tracking: %.0f%% | |
12 | + | ... | ... |
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/gui/resources/CalibrationInfoWindow_it.properties
0 → 100644
... | ... | @@ -0,0 +1,12 @@ |
1 | +#:1225964626,Francesco | |
2 | + | |
3 | +info.Action.text=Dettagli della calibrazione | |
4 | + | |
5 | +info.Action.shortDescription=Mostra i dettagli della calibrazione corrente. | |
6 | + | |
7 | +trackingAreaLabel.text=Area di tracciamento del Wiimote | |
8 | + | |
9 | +calibratedScreenLabel.text=Area di calibrazione schermo | |
10 | + | |
11 | +trackingUtilString=Utilizzo del tracciamento: %.0f%% | |
12 | + | ... | ... |
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/gui/resources/CalibrationInfoWindow_pl.properties
0 → 100644
... | ... | @@ -0,0 +1,12 @@ |
1 | +#:1253207905,Grzegorz | |
2 | + | |
3 | +info.Action.text=Szczeg\u00f3\u0142y kalibracji | |
4 | + | |
5 | +info.Action.shortDescription=Pokazuje szczeg\u00f3\u0142owe obszary wykonanej kalibracji. | |
6 | + | |
7 | +trackingAreaLabel.text=Pole widzenia Wiilot\u00f3w | |
8 | + | |
9 | +calibratedScreenLabel.text=Skalibrowane pole ekranu | |
10 | + | |
11 | +trackingUtilString=Optymalne wykorzystanie: %.0f%% | |
12 | + | ... | ... |
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/gui/resources/CalibrationInfoWindow_pt.properties
0 → 100644
... | ... | @@ -0,0 +1,12 @@ |
1 | +#:1223913940,Francisco | |
2 | + | |
3 | +info.Action.text=Detalhes da Calibra\u00e7\u00e3o | |
4 | + | |
5 | +info.Action.shortDescription=Apresenta os detalhes da presente calibra\u00e7\u00e3o. | |
6 | + | |
7 | +trackingAreaLabel.text=\u00c1rea de Visualiza\u00e7\u00e3o do Wiimote | |
8 | + | |
9 | +calibratedScreenLabel.text=\u00c1rea de Calibra\u00e7\u00e3o do Ecr\u00e3 | |
10 | + | |
11 | +trackingUtilString=Utiliza\u00e7\u00e3o da \u00c1rea de Visualiza\u00e7\u00e3o: %.0f%% | |
12 | + | ... | ... |
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/gui/resources/CalibrationInfoWindow_ru.properties
0 → 100644
... | ... | @@ -0,0 +1,7 @@ |
1 | +#Translated by Dmitry Pupinin (dlnsk@mail.ru) | |
2 | +#Mon Jan 25 00:14:24 NOVT 2010 | |
3 | +calibratedScreenLabel.text=\u041e\u0442\u043a\u0430\u043b\u0438\u0431\u0440\u043e\u0432\u0430\u043d\u043d\u0430\u044f \u043e\u0431\u043b\u0430\u0441\u0442\u044c \u044d\u043a\u0440\u0430\u043d\u0430 | |
4 | +trackingAreaLabel.text=\u041e\u0431\u043b\u0430\u0441\u0442\u044c \u0441\u043b\u0435\u0436\u0435\u043d\u0438\u044f Wiimote | |
5 | +trackingUtilString=\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u0430\u044f \u043e\u0431\u043b\u0430\u0441\u0442\u044c: %.0f%% | |
6 | +info.Action.text=\u041f\u043e\u0434\u0440\u043e\u0431\u043d\u043e\u0441\u0442\u0438 \u043a\u0430\u043b\u0438\u0431\u0440\u043e\u0432\u043a\u0438 | |
7 | +info.Action.shortDescription=\u041f\u043e\u043a\u0430\u0437\u0430\u0442\u044c \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u043e\u0441\u0442\u0438 \u043e \u0442\u0435\u043a\u0443\u0449\u0438\u0445 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\u0445 \u043a\u0430\u043b\u0438\u0431\u0440\u043e\u0432\u043a\u0438. | ... | ... |
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/gui/resources/CalibrationInfoWindow_sl.properties
0 → 100644
... | ... | @@ -0,0 +1,12 @@ |
1 | +#:1238601491,Samo | |
2 | + | |
3 | +info.Action.text=Podrobnosti kalibracije | |
4 | + | |
5 | +info.Action.shortDescription=Prika\u017ee podrobnosti veljavne kalibracije. | |
6 | + | |
7 | +trackingAreaLabel.text=Obmo\u010dje zaznavanja | |
8 | + | |
9 | +calibratedScreenLabel.text=Kalibrirano obmo\u010dje | |
10 | + | |
11 | +trackingUtilString=Izkoristek obmo\u010dja zaznavanja: %.0f%% | |
12 | + | ... | ... |
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/gui/resources/CameraMonitor.properties
0 → 100644
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/gui/resources/CameraMonitor_de.properties
0 → 100644
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/gui/resources/CameraMonitor_es.properties
0 → 100644
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/gui/resources/CameraMonitor_et.properties
0 → 100644
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/gui/resources/CameraMonitor_fr.properties
0 → 100644
... | ... | @@ -0,0 +1,7 @@ |
1 | +#:1224197209,Francois | |
2 | + | |
3 | +# 'Moniteur de la cam\u00e9ra IR' also OK? (need to save some space) | |
4 | +monitor.Action.text=Moniteur de la cam\u00e9ra IR | |
5 | + | |
6 | +monitor.Action.shortDescription=Vous indique ce que les cam\u00e9ras IR des Wiimotes enregistrent. Peut vous aider \u00e0 trouver la meilleure position pour les Wiimotes. | |
7 | + | ... | ... |
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/gui/resources/CameraMonitor_in.properties
0 → 100644
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/gui/resources/CameraMonitor_it.properties
0 → 100644
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/gui/resources/CameraMonitor_pl.properties
0 → 100644
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/gui/resources/CameraMonitor_pt.properties
0 → 100644
... | ... | @@ -0,0 +1,7 @@ |
1 | +#:1224239416,Francisco | |
2 | + | |
3 | +# 'Monitoriza\u00e7\u00e3o da C\u00e2mera de IV' also ok? | |
4 | +monitor.Action.text=Monitoriza\u00e7\u00e3o da C\u00e2mera de IV | |
5 | + | |
6 | +monitor.Action.shortDescription=Apresenta o que a c\u00e2mera de infravermelhos do Wiimote v\u00ea. Pode ajudar na escolha duma boa localiza\u00e7\u00e3o para o Wiimote. | |
7 | + | ... | ... |
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/gui/resources/CameraMonitor_ru.properties
0 → 100644
... | ... | @@ -0,0 +1,4 @@ |
1 | +#Translated by Dmitry Pupinin (dlnsk@mail.ru) | |
2 | +#Mon Jan 25 00:14:24 NOVT 2010 | |
3 | +monitor.Action.shortDescription=\u041f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442 \u0442\u043e, \u0447\u0442\u043e \u0432\u0438\u0434\u0438\u0442 \u0418\u041a-\u043a\u0430\u043c\u0435\u0440\u0430. \u041c\u043e\u0436\u0435\u0442 \u043f\u043e\u043c\u043e\u0447\u044c \u0432\u0430\u043c \u043d\u0430\u0439\u0442\u0438 \u0445\u043e\u0440\u043e\u0448\u0443\u044e \u043f\u043e\u0437\u0438\u0446\u0438\u044e \u0434\u043b\u044f Wiimote. | |
4 | +monitor.Action.text=\u041c\u043e\u043d\u0438\u0442\u043e\u0440\u0438\u043d\u0433 \u0418\u041a-\u043a\u0430\u043c\u0435\u0440\u044b | ... | ... |
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/gui/resources/CameraMonitor_sl.properties
0 → 100644
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/gui/resources/Credits.html
0 → 100644
... | ... | @@ -0,0 +1,86 @@ |
1 | +<html> | |
2 | +<body> | |
3 | + | |
4 | +<div style="text-align: center; font-family: Lucida Grande, Lucida Sans, Verdana; font-size: 11pt;"> | |
5 | + | |
6 | +<p> | |
7 | +<b>Website</b><br> | |
8 | +<a href="http://www.uweschmidt.org/wiimote-whiteboard">uweschmidt.org/wiimote-whiteboard</a> | |
9 | +</p> | |
10 | + | |
11 | +<p> | |
12 | +<b>Developer</b><br> | |
13 | +Uwe Schmidt | |
14 | +</p> | |
15 | + | |
16 | +<p> | |
17 | +<b>Based on</b><br> | |
18 | +WiimoteWhiteboard by <a href="http://johnnylee.net/projects/wii/">Johnny Lee</a> | |
19 | +</p> | |
20 | + | |
21 | +<p> | |
22 | +<b>Translators</b><br> | |
23 | +<a href="http://estmarx.pri.ee">Marko Puusaar<a> (Estonian)<br> | |
24 | +<a href="http://perso.univ-lyon2.fr/~fbocquet/">Francois Bocquet<a> (French)<br> | |
25 | +<a href="http://www.uweschmidt.org">Uwe Schmidt</a> (German)<br> | |
26 | +<a href="http://bobbyprabowo.wordpress.com">Bobby Adi Prabowo</a> (Indonesian)<br> | |
27 | +<a href="http://fradelpra.altervista.org">Francesco Del Prato</a>, <a href="http://nilocram.altervista.org/spip/index.php3">Roberto Marcolin</a> (Italian)<br> | |
28 | +<a href="http://edukom.sytes.net/">Grzegorz Ciaglo</a> (Polish)<br> | |
29 | +<a href="http://clinik.net/wiimote/">Francisco Cardoso Lima</a> (Portuguese)<br> | |
30 | +Dmitry Pupinin (Russian)<br> | |
31 | +Samo Vesel (Slovene)<br> | |
32 | +Peter Skouson (Spanish)<br> | |
33 | +</p> | |
34 | + | |
35 | +<p> | |
36 | +<b>Third Party Software</b><br> | |
37 | +<a href="http://www.centerkey.com/java/browser/">Bare Bones Browser Launch<a><br> | |
38 | +<a href="http://www.bluecove.org/">BlueCove</a><br> | |
39 | +<a href="https://jai-core.dev.java.net/">Java Advanced Imaging</a><br> | |
40 | +<a href="http://www.miglayout.com/">MiGLayout</a><br> | |
41 | +<a href="http://www.sciss.de/netutil/">NetUtil</a><br> | |
42 | +<a href="http://developer.apple.com/samplecode/OSXAdapter/listing3.html">OSXAdapter</a><br> | |
43 | +<a href="https://appframework.dev.java.net/">Swing Application Framework</a><br> | |
44 | +<a href="https://swingworker.dev.java.net/">Swing Worker</a><br> | |
45 | +<a href="http://www.world-of-cha0s.hostrocket.com/WiiRemoteJ/">WiiRemoteJ</a><br> | |
46 | +</p> | |
47 | + | |
48 | +<p> | |
49 | +<b>Icons</b><br> | |
50 | +<a href="http://www.everaldo.com/crystal/">Crystal Project<a><br> | |
51 | +<a href="http://clinik.net/wiimote/">Francisco Cardoso Lima</a> - Application Icon, Calibration Icons | |
52 | +</p> | |
53 | + | |
54 | +<p> | |
55 | +<b>Warranty Information</b><br> | |
56 | +This software is licensed<br> | |
57 | +under a modified MIT License: | |
58 | +</p> | |
59 | + | |
60 | +<div style="color: gray; padding: 5px; text-align: justify;"> | |
61 | +Permission is hereby granted, free of charge, to any person obtaining a | |
62 | +copy of this software and associated documentation files (the "Software"), | |
63 | +to deal in the Software without restriction, including without limitation | |
64 | +the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
65 | +and/or sell copies of the Software, and to permit persons to whom the | |
66 | +Software is furnished to do so, subject to the following conditions: <br><br> | |
67 | + | |
68 | +The above copyright notice and this permission notice shall be included in | |
69 | +all copies or substantial portions of the Software. <br><br> | |
70 | + | |
71 | +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
72 | +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
73 | +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
74 | +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
75 | +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |
76 | +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | |
77 | +DEALINGS IN THE SOFTWARE. <br><br> | |
78 | + | |
79 | +The Software uses a third-party library (WiiRemoteJ) which is not part of | |
80 | +the Software and is subject to its own license. | |
81 | +</div> | |
82 | +</div> | |
83 | + | |
84 | + | |
85 | +</body> | |
86 | +</html> | |
0 | 87 | \ No newline at end of file | ... | ... |
WiimoteWhiteboard/src/org/mote/wiimote/whiteboard/gui/resources/HelpHandler.properties
0 → 100644