Source code: com/arranger/jarl/stroke/base/Sin.java
1 package com.arranger.jarl.stroke.base;
2
3 import com.arranger.jarl.base.IContext;
4 import com.arranger.jarl.base.IJarlObjectInfo;
5 import com.arranger.jarl.stroke.BaseSegmentStroke;
6 import com.arranger.jarl.util.*;
7 import com.arranger.jarl.widget.IWidget;
8 import org.w3c.dom.Element;
9
10 import java.awt.*;
11 import java.awt.geom.Point2D;
12
13 /**
14 * Sin creates a sin curve instead of the normal outline
15 *
16 * @strokeAttribute frequency ## xs:float ## the frequency of the sin curve
17 * @strokeAttribute amplitude ## xs:float ## the amplitude of the sin curve
18 * @strokeAttribute rand ## xs:float ## the amount of randomness to apply to the amplitude (less then 0 means no rand)
19 *
20 * @strokeAttribute startFrequency ## xs:float ## the starting frequency
21 * @strokeAttribute endFrequency ## xs:float ## the ending frequency
22 * @strokeAttribute startAmplitude ## xs:float ## the starting amplitude
23 * @strokeAttribute endAmplitude ## xs:float ## the ending amplitude
24 * @strokeAttribute startRand ## xs:float ## the starting amount of randomness to apply to the amplitude (less then 0 means no rand)
25 * @strokeAttribute endRand ## xs:float ## the ending amount of randomness to apply to the amplitude (less then 0 means no rand)
26 */
27 public class Sin extends BaseSegmentStroke {
28
29 protected double m_frequency = Double.NEGATIVE_INFINITY;
30 protected double m_amplitude = Double.NEGATIVE_INFINITY;
31 protected double m_rand = Double.NEGATIVE_INFINITY;
32
33 protected double m_startFrequency = Double.NEGATIVE_INFINITY;
34 protected double m_startAmplitude = Double.NEGATIVE_INFINITY;
35 protected double m_endFrequency = Double.NEGATIVE_INFINITY;
36 protected double m_endAmplitude = Double.NEGATIVE_INFINITY;
37 protected double m_startRand = Double.NEGATIVE_INFINITY;
38 protected double m_endRand = Double.NEGATIVE_INFINITY;
39
40
41 /**
42 * This will perform custom drawing of the shape
43 * onto the graphics on behalf of the widget
44 *
45 * @param widget
46 * @param context
47 * @param graphics2D
48 * @param shape
49 */
50 public Shape processShape(IWidget widget,
51 IContext context,
52 Graphics2D graphics2D,
53 Shape shape) {
54 double currentFrequency;
55 double currentAmplitude;
56 double currentRand;
57
58 //update stuff
59 if (m_frequency != Double.NEGATIVE_INFINITY && m_amplitude != Double.NEGATIVE_INFINITY && m_rand != Double.NEGATIVE_INFINITY) {
60 currentFrequency = m_frequency;
61 currentAmplitude = m_amplitude;
62 currentRand = m_rand;
63 } else if (m_startFrequency != Double.NEGATIVE_INFINITY &&
64 m_startAmplitude != Double.NEGATIVE_INFINITY &&
65 m_endFrequency != Double.NEGATIVE_INFINITY &&
66 m_endAmplitude != Double.NEGATIVE_INFINITY &&
67 m_startRand != Double.NEGATIVE_INFINITY &&
68 m_endRand != Double.NEGATIVE_INFINITY) {
69
70 currentFrequency = InterpolateUtil.interpolate(widget.getStartTime(),
71 widget.getEndTime(),
72 m_startFrequency,
73 m_endFrequency,
74 context.getTime());
75
76 currentAmplitude = InterpolateUtil.interpolate(widget.getStartTime(),
77 widget.getEndTime(),
78 m_startAmplitude,
79 m_endAmplitude,
80 context.getTime());
81
82 currentRand = InterpolateUtil.interpolate(widget.getStartTime(),
83 widget.getEndTime(),
84 m_startRand,
85 m_endRand,
86 context.getTime());
87 } else {
88 SinConfigSegment sinConfigSegment = (SinConfigSegment) getCurrentSegment(widget, context);
89 double currentPct = getCurrentSegmentTimePct(widget, context, sinConfigSegment);
90
91 currentFrequency = InterpolateUtil.interpolate(0,
92 1,
93 sinConfigSegment.getStartFrequency(),
94 sinConfigSegment.getEndFrequency(),
95 currentPct);
96 currentAmplitude = InterpolateUtil.interpolate(0,
97 1,
98 sinConfigSegment.getStartAmplitude(),
99 sinConfigSegment.getEndAmplitude(),
100 currentPct);
101 currentRand = InterpolateUtil.interpolate(0,
102 1,
103 sinConfigSegment.getStartRand(),
104 sinConfigSegment.getEndRand(),
105 currentPct);
106 }
107
108 Point2D[] points = WidgetUtil.transform(shape,
109 new SinWidgetTransfrom(currentFrequency,
110 currentAmplitude,
111 currentRand));
112 return WidgetUtil.pointsToShape(points, false);
113 }
114
115 /**
116 * Create a concrete {@link WidgetConfigSegment} based on this element
117 * @param element
118 * @return a specific {@link WidgetConfigSegment}
119 */
120 public WidgetConfigSegment createSegment(Element element) {
121 return new SinConfigSegment(element);
122 }
123
124 /**
125 * Always remember some attrs might not be there
126 * @param context
127 */
128 protected void initAttributes(IContext context) {
129 super.initAttributes(context);
130
131 ObjectUtil.initializeField("frequency", m_configElement, this, ObjectUtil.PRIMITIVE_CONVERSION);
132 ObjectUtil.initializeField("amplitude", m_configElement, this, ObjectUtil.PRIMITIVE_CONVERSION);
133 ObjectUtil.initializeField("rand", m_configElement, this, ObjectUtil.PRIMITIVE_CONVERSION);
134
135 ObjectUtil.initializeField("startFrequency", m_configElement, this, ObjectUtil.PRIMITIVE_CONVERSION);
136 ObjectUtil.initializeField("startAmplitude", m_configElement, this, ObjectUtil.PRIMITIVE_CONVERSION);
137 ObjectUtil.initializeField("startRand", m_configElement, this, ObjectUtil.PRIMITIVE_CONVERSION);
138 ObjectUtil.initializeField("endAmplitude", m_configElement, this, ObjectUtil.PRIMITIVE_CONVERSION);
139 ObjectUtil.initializeField("amplitude", m_configElement, this, ObjectUtil.PRIMITIVE_CONVERSION);
140 ObjectUtil.initializeField("endRand", m_configElement, this, ObjectUtil.PRIMITIVE_CONVERSION);
141 }
142
143 /**
144 * Override this, and for every field that you're using, call {@link #populateInfo}
145 * for example:
146 * <code>
147 * populateInfo(jarlObjectInfo, "zOrder", "Z-Order", JarlInfoUtil.PRIMITIVE_DISPLAY);
148 * </code>
149 *
150 * @param jarlObjectInfo
151 *
152 * @see JarlInfoUtil#PRIMITIVE_DISPLAY
153 * @see #populateInfo
154 * @see ObjectUtil#initializeField
155 */
156 protected void addJarlObjectInfo(IJarlObjectInfo jarlObjectInfo) {
157 super.addJarlObjectInfo(jarlObjectInfo);
158 populateInfo(jarlObjectInfo, "frequency", "Frequency", JarlInfoUtil.PRIMITIVE_DISPLAY);
159 populateInfo(jarlObjectInfo, "amplitude", "Amplitude", JarlInfoUtil.PRIMITIVE_DISPLAY);
160 populateInfo(jarlObjectInfo, "rand", "Random (seed)", JarlInfoUtil.PRIMITIVE_DISPLAY);
161
162 populateInfo(jarlObjectInfo, "startFrequency", "Start Frequency", JarlInfoUtil.PRIMITIVE_DISPLAY);
163 populateInfo(jarlObjectInfo, "startAmplitude", "Start Amplitude", JarlInfoUtil.PRIMITIVE_DISPLAY);
164 populateInfo(jarlObjectInfo, "startRand", "Start Random (seed)", JarlInfoUtil.PRIMITIVE_DISPLAY);
165 populateInfo(jarlObjectInfo, "endFrequency", "End Frequency", JarlInfoUtil.PRIMITIVE_DISPLAY);
166 populateInfo(jarlObjectInfo, "endAmplitude", "End Amplitude", JarlInfoUtil.PRIMITIVE_DISPLAY);
167 populateInfo(jarlObjectInfo, "endRand", "End Random (seed)", JarlInfoUtil.PRIMITIVE_DISPLAY);
168 }
169
170 protected static class SinConfigSegment extends WidgetConfigSegment {
171
172 protected double m_startFrequency = Double.NEGATIVE_INFINITY;
173 protected double m_startAmplitude = Double.NEGATIVE_INFINITY;
174 protected double m_endFrequency = Double.NEGATIVE_INFINITY;
175 protected double m_endAmplitude = Double.NEGATIVE_INFINITY;
176 protected double m_startRand = Double.NEGATIVE_INFINITY;
177 protected double m_endRand = Double.NEGATIVE_INFINITY;
178
179 public SinConfigSegment(Element element) {
180 super(element);
181
182 ObjectUtil.initializeFieldStrict("startFrequency", element, this, ObjectUtil.PRIMITIVE_CONVERSION);
183 ObjectUtil.initializeFieldStrict("startAmplitude", element, this, ObjectUtil.PRIMITIVE_CONVERSION);
184 ObjectUtil.initializeFieldStrict("startRand", element, this, ObjectUtil.PRIMITIVE_CONVERSION);
185 ObjectUtil.initializeFieldStrict("endFrequency", element, this, ObjectUtil.PRIMITIVE_CONVERSION);
186 ObjectUtil.initializeFieldStrict("endAmplitude", element, this, ObjectUtil.PRIMITIVE_CONVERSION);
187 ObjectUtil.initializeFieldStrict("startRand", element, this, ObjectUtil.PRIMITIVE_CONVERSION);
188 ObjectUtil.initializeFieldStrict("endRand", element, this, ObjectUtil.PRIMITIVE_CONVERSION);
189 }
190
191 protected void addJarlObjectInfo(IJarlObjectInfo jarlObjectInfo) {
192 super.addJarlObjectInfo(jarlObjectInfo);
193 populateInfo(jarlObjectInfo, "startFrequency", "Start Frequency", JarlInfoUtil.PRIMITIVE_DISPLAY);
194 populateInfo(jarlObjectInfo, "startAmplitude", "Start Amplitude", JarlInfoUtil.PRIMITIVE_DISPLAY);
195 populateInfo(jarlObjectInfo, "startRand", "Start Random (seed)", JarlInfoUtil.PRIMITIVE_DISPLAY);
196 populateInfo(jarlObjectInfo, "endFrequency", "End Frequency", JarlInfoUtil.PRIMITIVE_DISPLAY);
197 populateInfo(jarlObjectInfo, "endAmplitude", "End Amplitude", JarlInfoUtil.PRIMITIVE_DISPLAY);
198 populateInfo(jarlObjectInfo, "endRand", "End Random (seed)", JarlInfoUtil.PRIMITIVE_DISPLAY);
199 }
200
201 public double getStartFrequency() {
202 return m_startFrequency;
203 }
204
205 public double getStartAmplitude() {
206 return m_startAmplitude;
207 }
208
209 public double getEndFrequency() {
210 return m_endFrequency;
211 }
212
213 public double getEndAmplitude() {
214 return m_endAmplitude;
215 }
216
217 public double getStartRand() {
218 return m_startRand;
219 }
220
221 public double getEndRand() {
222 return m_endRand;
223 }
224 }
225
226 public static class SinWidgetTransfrom implements IWidgetTransform {
227 protected double m_currentFrequency;
228 protected double m_currentAmplitude;
229 protected double m_currentRand = 1.0;
230
231 public SinWidgetTransfrom(double currentFrequency, double currentAmplitude, double currentRand) {
232 m_currentFrequency = currentFrequency;
233 m_currentAmplitude = currentAmplitude;
234 m_currentRand = currentRand;
235 }
236
237 /**
238 * Initialize the transform, returning the offset if needed
239 *
240 * @param shapePoints the original shape points
241 * @return the offset into the array of points. return 0 for no offset
242 */
243 public double initializeTransform(Point2D[] shapePoints) {
244 return 0;
245 }
246
247 /**
248 * Generate a series of points that will be used to transform
249 * other lines
250 *
251 * @param distance the total x requested (pt[N].x - pt[0].x == x)
252 * @param offset an offset into the internal algortihm
253 * @param precision how frequent to get points
254 * @return an arry of points of the form: x1, y1, x2, y2, .... xN, yN
255 */
256 public double[] getPoints(double distance, double offset, double precision) {
257 double[] points = new double[(int) ((distance / precision) + 1) * 2];
258
259 int writeIndex = 0;
260 for (double x = 0; x <= distance; x += precision) {
261 double degrees = (offset + x) * m_currentFrequency;
262 double y = Math.sin(Math.toRadians(degrees)) * m_currentAmplitude;
263 if (m_currentRand > 0) {
264 y *= RandomUtil.getRandomRange(1.0, m_currentRand);
265 }
266
267 points[writeIndex++] = x;
268 points[writeIndex++] = y;
269 }
270
271 if (writeIndex != points.length) {
272 double[] filteredPoints = new double[writeIndex];
273 System.arraycopy(points, 0, filteredPoints, 0, writeIndex);
274 points = filteredPoints;
275 }
276
277 return points;
278 }
279
280 /**
281 * Whether or not to reverse through the points
282 * @return true for reversing, false for normal
283 */
284 public boolean isReverse() {
285 return false;
286 }
287 }
288 }