Created
April 23, 2012 14:40
-
-
Save leeprobert/2471336 to your computer and use it in GitHub Desktop.
A class for drawing a block of text and then drawing an extrusion path from the bounds of the rectangle
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.cc.math | |
{ | |
/** | |
* Full details: http://gamedev.michaeljameswilliams.com/2009/04/24/angles-in-flash/ | |
* Hungarian notation idea: http://www.joelonsoftware.com/articles/Wrong.html | |
* Optimisation credit: http://www.nbilyk.com/optimizing-actionscript-3 | |
* | |
* Usage: import com.michaeljameswilliams.gamedev.angles.Angle | |
* var degAngle:Number = Angle.degFromRad( radAngle ); | |
* | |
* @author MichaelJWilliams | |
*/ | |
public class Angle | |
{ | |
private static const localPi:Number = Math.PI; | |
private static const localTwoPi:Number = Math.PI * 2; | |
private static const oneOver180:Number = 1 / 180; | |
private static const oneOverPi:Number = 1 / localPi; | |
public function Angle() | |
{ | |
trace( "The com.michaeljameswilliams.gamedev.angles.Angle class does not need to be instantiated" ); | |
} | |
/** | |
* @param p_degInput Angle, in degrees | |
* @return Angle, in degrees, in range 0 to 360 | |
*/ | |
public static function degFromDeg( p_degInput:Number ):Number | |
{ | |
var degOutput:Number = p_degInput; | |
while ( degOutput >= 360 ) | |
{ | |
degOutput -= 360; | |
} | |
while ( degOutput < 0 ) | |
{ | |
degOutput += 360; | |
} | |
return degOutput; | |
} | |
/** | |
* @param p_rotInput Angle, in degrees | |
* @return Angle, in degrees, in range -180 to + 180 | |
*/ | |
public static function rotFromRot( p_rotInput:Number ):Number | |
{ | |
var rotOutput:Number = p_rotInput; | |
while ( rotOutput > 180 ) | |
{ | |
rotOutput -= 360; | |
} | |
while ( rotOutput < -180 ) | |
{ | |
rotOutput += 360; | |
} | |
return rotOutput; | |
} | |
/** | |
* @param p_radInput Angle, in radians | |
* @return Angle, in radians, in range 0 to ( 2 * pi ) | |
*/ | |
public static function radFromRad( p_radInput:Number ):Number | |
{ | |
var radOutput:Number = p_radInput; | |
while ( radOutput >= localTwoPi ) | |
{ | |
radOutput -= localTwoPi; | |
} | |
while ( radOutput < -localTwoPi ) | |
{ | |
radOutput += localTwoPi; | |
} | |
return radOutput; | |
} | |
/** | |
* @param p_degInput Angle, in degrees | |
* @return Angle, in degrees, in range -180 to +180 | |
*/ | |
public static function rotFromDeg( p_degInput:Number ):Number | |
{ | |
var rotOutput:Number = p_degInput; | |
while ( rotOutput > 180 ) | |
{ | |
rotOutput -= 360; | |
} | |
while ( rotOutput < -180 ) | |
{ | |
rotOutput += 360; | |
} | |
return rotOutput; | |
} | |
/** | |
* @param p_rotInput Angle, in degrees | |
* @return Angle, in degrees, in range 0 to 360 | |
*/ | |
public static function degFromRot( p_rotInput:Number ):Number | |
{ | |
var degOutput:Number = p_rotInput; | |
while ( degOutput >= 360 ) | |
{ | |
degOutput -= 360; | |
} | |
while ( degOutput < 0 ) | |
{ | |
degOutput += 360; | |
} | |
return degOutput; | |
} | |
/** | |
* @param p_radInput Angle, in radians | |
* @return Angle, in degrees, in range 0 to 360 | |
*/ | |
public static function degFromRad( p_radInput:Number ):Number | |
{ | |
var degOutput:Number = 180 * oneOverPi * radFromRad( p_radInput ); | |
return degOutput; | |
} | |
/** | |
* @param p_degInput Angle, in degrees | |
* @return Angle, in radians, in range 0 to ( 2 * pi ) | |
*/ | |
public static function radFromDeg( p_degInput:Number ):Number | |
{ | |
var radOutput:Number = localPi * oneOver180 * degFromDeg( p_degInput ); | |
return radOutput; | |
} | |
/** | |
* @param p_radInput Angle, in radians | |
* @return Angle, in degrees, in range -180 to +180 | |
*/ | |
public static function rotFromRad( p_radInput:Number ):Number | |
{ | |
return rotFromDeg( degFromRad( p_radInput ) ); | |
} | |
/** | |
* @param p_rotInput Angle, in degrees | |
* @return Angle, in radians, in range 0 to ( 2 * pi ) | |
*/ | |
public static function radFromRot( p_rotInput:Number ):Number | |
{ | |
return radFromDeg( degFromRot( p_rotInput ) ); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.cc.math | |
{ | |
/** | |
* Full details: http://gamedev.michaeljameswilliams.com/2009/04/24/angles-in-flash/ | |
* Hungarian notation idea: http://www.joelonsoftware.com/articles/Wrong.html | |
* Optimisation credit: http://www.nbilyk.com/optimizing-actionscript-3 | |
* | |
* Use this instead of the Angle functions when you need extra speed | |
* and are going to use the functions multiple times (e.g. over a loop) | |
* | |
* Usage: import com.michaeljameswilliams.gamedev.angles.Angle | |
* var angleUtil:AngleUtil = new AngleUtil(); | |
* var degAngle:Number = angleUtil.degFromRad( radAngle ); | |
* | |
* @author MichaelJWilliams | |
*/ | |
public class AngleUtil | |
{ | |
private const localPi:Number = Math.PI; | |
private const localTwoPi:Number = Math.PI * 2; | |
private const oneOver180:Number = 1 / 180; | |
private const oneOverPi:Number = 1 / localPi; | |
public function AngleUtil() | |
{ | |
} | |
/** | |
* @param p_degInput Angle, in degrees | |
* @return Angle, in degrees, in range 0 to 360 | |
*/ | |
public function degFromDeg( p_degInput:Number ):Number | |
{ | |
var degOutput:Number = p_degInput; | |
while ( degOutput >= 360 ) | |
{ | |
degOutput -= 360; | |
} | |
while ( degOutput < 0 ) | |
{ | |
degOutput += 360; | |
} | |
return degOutput; | |
} | |
/** | |
* @param p_rotInput Angle, in degrees | |
* @return Angle, in degrees, in range -180 to + 180 | |
*/ | |
public function rotFromRot( p_rotInput:Number ):Number | |
{ | |
var rotOutput:Number = p_rotInput; | |
while ( rotOutput > 180 ) | |
{ | |
rotOutput -= 360; | |
} | |
while ( rotOutput < -180 ) | |
{ | |
rotOutput += 360; | |
} | |
return rotOutput; | |
} | |
/** | |
* @param p_radInput Angle, in radians | |
* @return Angle, in radians, in range 0 to ( 2 * pi ) | |
*/ | |
public function radFromRad( p_radInput:Number ):Number | |
{ | |
var radOutput:Number = p_radInput; | |
while ( radOutput >= localTwoPi ) | |
{ | |
radOutput -= localTwoPi; | |
} | |
while ( radOutput < -localTwoPi ) | |
{ | |
radOutput += localTwoPi; | |
} | |
return radOutput; | |
} | |
/** | |
* @param p_degInput Angle, in degrees | |
* @return Angle, in degrees, in range -180 to +180 | |
*/ | |
public function rotFromDeg( p_degInput:Number ):Number | |
{ | |
var rotOutput:Number = p_degInput; | |
while ( rotOutput > 180 ) | |
{ | |
rotOutput -= 360; | |
} | |
while ( rotOutput < -180 ) | |
{ | |
rotOutput += 360; | |
} | |
return rotOutput; | |
} | |
/** | |
* @param p_rotInput Angle, in degrees | |
* @return Angle, in degrees, in range 0 to 360 | |
*/ | |
public function degFromRot( p_rotInput:Number ):Number | |
{ | |
var degOutput:Number = p_rotInput; | |
while ( degOutput >= 360 ) | |
{ | |
degOutput -= 360; | |
} | |
while ( degOutput < 0 ) | |
{ | |
degOutput += 360; | |
} | |
return degOutput; | |
} | |
/** | |
* @param p_radInput Angle, in radians | |
* @return Angle, in degrees, in range 0 to 360 | |
*/ | |
public function degFromRad( p_radInput:Number ):Number | |
{ | |
var degOutput:Number = 180 * oneOverPi * radFromRad( p_radInput ); | |
return degOutput; | |
} | |
/** | |
* @param p_degInput Angle, in degrees | |
* @return Angle, in radians, in range 0 to ( 2 * pi ) | |
*/ | |
public function radFromDeg( p_degInput:Number ):Number | |
{ | |
var radOutput:Number = localPi * oneOver180 * degFromDeg( p_degInput ); | |
return radOutput; | |
} | |
/** | |
* @param p_radInput Angle, in radians | |
* @return Angle, in degrees, in range -180 to +180 | |
*/ | |
public function rotFromRad( p_radInput:Number ):Number | |
{ | |
return rotFromDeg( degFromRad( p_radInput ) ); | |
} | |
/** | |
* @param p_rotInput Angle, in degrees | |
* @return Angle, in radians, in range 0 to ( 2 * pi ) | |
*/ | |
public function radFromRot( p_rotInput:Number ):Number | |
{ | |
return radFromDeg( degFromRot( p_rotInput ) ); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8"?> | |
<s:Group | |
xmlns:fx="http://ns.adobe.com/mxml/2009" | |
xmlns:s="library://ns.adobe.com/flex/spark" | |
xmlns:mx="library://ns.adobe.com/flex/mx" | |
creationComplete="group1_creationCompleteHandler(event)" | |
> | |
<fx:Script> | |
<![CDATA[ | |
import com.cc.math.Angle; | |
import com.cc.math.VMath; | |
import com.cc.math.Vector2D; | |
import mx.collections.ArrayCollection; | |
import mx.events.FlexEvent; | |
import mx.states.OverrideBase; | |
protected var _pathBlendMode:String = BlendMode.NORMAL; | |
[Bindable] | |
public function get pathBlendMode():String { return _pathBlendMode; }; | |
public function set pathBlendMode(value:String):void { _pathBlendMode = value; }; | |
protected var _pathAlpha:Number = 0.5; | |
[Bindable] | |
public function get pathAlpha():Number { return _pathAlpha; }; | |
public function set pathAlpha(value:Number):void { _pathAlpha = value; }; | |
protected var _text:String; | |
[Bindable] | |
public function get text():String { return _text; }; | |
public function set text(value:String):void { _text = value; }; | |
protected var _labelStyleName:String; | |
[Bindable] | |
public function get labelStyleName():String { return _labelStyleName; }; | |
public function set labelStyleName(value:String):void { _labelStyleName = value; }; | |
protected var _maxLabelWidth:Number; | |
[Bindable] | |
public function get maxLabelWidth():Number { return _maxLabelWidth; }; | |
public function set maxLabelWidth(value:Number):void { _maxLabelWidth = value; }; | |
protected var _labelPadding:Number = 6; | |
[Bindable] | |
public function get labelPadding():Number { return _labelPadding; }; | |
public function set labelPadding(value:Number):void { _labelPadding = value; }; | |
protected var _extrusionAmount:Number = 200; | |
[Bindable] | |
public function get extrusionAmount():Number { return _extrusionAmount; }; | |
public function set extrusionAmount(value:Number):void { _extrusionAmount = value; }; | |
protected var _extrusionAngle:Number = 290; // point North East (zero is east) | |
[Bindable] | |
public function get extrusionAngle():Number { return _extrusionAngle; }; | |
public function set extrusionAngle(value:Number):void | |
{ | |
// snap the angle to within defined range avoiding right-angles and setting the quadrant | |
if(value <=89.999) | |
{ | |
if(value < 10) value = 10; | |
else if(value > 80) value = 80; | |
quadrantNumber = 1; | |
} | |
else if(value >= 90 && value <=179.999) | |
{ | |
if(value < 100) value = 100; | |
else if(value > 170) value = 170; | |
quadrantNumber = 2; | |
} | |
else if(value >= 180 && value <=269.999) | |
{ | |
if(value < 190) value = 190; | |
else if(value > 260) value = 260; | |
quadrantNumber = 3; | |
} | |
else if(value >= 270) | |
{ | |
if(value < 280) value = 280; | |
else if(value > 350) value = 350; | |
quadrantNumber = 4; | |
} | |
_extrusionAngle = value; | |
}; | |
protected var _quadrantNumber:int = 4; // the default angle is 290 so we are in Q4 | |
[Bindable] | |
public function get quadrantNumber():int { return _quadrantNumber; }; | |
public function set quadrantNumber(value:int):void { _quadrantNumber = value; }; | |
[Bindable] | |
protected var vo:Vector2D; | |
[Bindable] | |
protected var v1:Vector2D; | |
[Bindable] | |
protected var v2:Vector2D; | |
[Bindable] | |
protected var v3:Vector2D; | |
[Bindable] | |
protected var v4:Vector2D; | |
[Bindable] | |
protected var v5:Vector2D; | |
[Bindable] | |
protected var v6:Vector2D; | |
protected var adj:Number; | |
protected var theta:Number; | |
protected var hyp:Number; | |
protected var opp:Number; | |
protected function group1_creationCompleteHandler(event:FlexEvent):void | |
{ | |
drawExtrusion(); | |
} | |
//------------------------------------------------------------- | |
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void | |
{ | |
drawExtrusion(); | |
super.updateDisplayList(unscaledWidth, unscaledHeight); | |
} | |
//---------------------------------------------------------------------------- | |
public function drawExtrusion():void | |
{ | |
if(rect.width && rect.height) | |
{ | |
var sv1:Vector2D; | |
var sv2:Vector2D; | |
var sv3:Vector2D; | |
var sv4:Vector2D; | |
var sv5:Vector2D; | |
/* | |
Create an origin vector for the quadrants that start at zero zero. | |
Quadrant 3 starts at the rect width for X and zero for Y. | |
*/ | |
vo = new Vector2D(0,0); | |
/* | |
Get the length of the hypotenuse and the opposite in the extra triangle we need to draw | |
to extrude the triangle down to the edges. | |
*/ | |
var hypLength:Number = Math.abs(rect.width/Math.cos(Angle.radFromDeg(extrusionAngle))); | |
var oppLength:Number = Math.abs(hypLength*Math.sin(Angle.radFromDeg(extrusionAngle))); | |
/* | |
Now create the vectors used to draw the paths based on the quadrant we are in | |
*/ | |
switch(quadrantNumber) | |
{ | |
case 1: | |
// vector ONE | |
v1 = new Vector2D(); | |
v1.length = rect.width; | |
v1.angle = Angle.radFromDeg(0); | |
// vector TWO | |
sv2 = new Vector2D(); | |
sv2.length = extrusionAmount+hypLength; | |
sv2.angle = Angle.radFromDeg(extrusionAngle); | |
VMath.reverse(sv2); | |
v2 = VMath.add(v1,sv2); | |
// vector THREE | |
sv3 = new Vector2D(); | |
sv3.length = rect.height+oppLength; | |
sv3.angle = Angle.radFromDeg(90); | |
v3 = VMath.add(v2,sv3); | |
// point FOUR | |
sv4 = new Vector2D(); | |
sv4.length = extrusionAmount; | |
sv4.angle = Angle.radFromDeg(extrusionAngle); | |
v4 = VMath.add(v3,sv4); | |
// point FIVE | |
sv5 = new Vector2D(); | |
sv5.length = rect.height; | |
sv5.angle = Angle.radFromDeg(270); | |
v5 = VMath.add(v4,sv5); | |
break; | |
//------------------- | |
case 2: | |
// vector ONE | |
v1 = new Vector2D(); | |
v1.length = extrusionAmount+hypLength; | |
v1.angle = Angle.radFromDeg(extrusionAngle); | |
VMath.reverse(v1); | |
// vector TWO | |
sv2 = new Vector2D(); | |
sv2.length = rect.height+oppLength; | |
sv2.angle = Angle.radFromDeg(90); | |
v2 = VMath.add(v1,sv2); | |
// vector THREE | |
sv3 = new Vector2D(); | |
sv3.length = extrusionAmount; | |
sv3.angle = Angle.radFromDeg(extrusionAngle); | |
v3 = VMath.add(v2,sv3); | |
// point FOUR | |
sv4 = new Vector2D(); | |
sv4.length = rect.height; | |
sv4.angle = Angle.radFromDeg(270); | |
v4 = VMath.add(v3,sv4); | |
// point FIVE | |
sv5 = new Vector2D(); | |
sv5.length = rect.width; | |
sv5.angle = Angle.radFromDeg(180); | |
v5 = VMath.add(v4,sv5); | |
break; | |
//------------------- | |
case 3: | |
// vector ORIGIN | |
vo = new Vector2D(); | |
vo.length = rect.width; | |
vo.angle = 0; | |
// vector ONE | |
sv1 = new Vector2D(); | |
sv1.length = extrusionAmount; | |
sv1.angle = Angle.radFromDeg(extrusionAngle); | |
VMath.reverse(sv1); | |
v1 = VMath.add(vo,sv1); | |
// vector TWO | |
sv2 = new Vector2D(); | |
sv2.length = rect.height+oppLength; | |
sv2.angle = Angle.radFromDeg(90); | |
v2 = VMath.add(v1,sv2); | |
// vector THREE | |
sv3 = new Vector2D(); | |
sv3.length = extrusionAmount+hypLength; | |
sv3.angle = Angle.radFromDeg(extrusionAngle); | |
v3 = VMath.add(v2,sv3); | |
// point FOUR | |
sv4 = new Vector2D(); | |
sv4.length = rect.width; | |
sv4.angle = Angle.radFromDeg(0); | |
v4 = VMath.add(v3,sv4); | |
// point FIVE | |
sv5 = new Vector2D(); | |
sv5.length = rect.height; | |
sv5.angle = Angle.radFromDeg(270); | |
v5 = VMath.add(v4,sv5); | |
break; | |
//------------------- | |
case 4: | |
// vector ONE | |
v1 = new Vector2D(); | |
v1.length = rect.height; | |
v1.angle = Angle.radFromDeg(90); | |
// vector TWO | |
sv2 = new Vector2D(); | |
sv2.length = rect.width; | |
sv2.angle = Angle.radFromDeg(0); | |
v2 = VMath.add(v1,sv2); | |
// vector THREE | |
sv3 = new Vector2D(); | |
sv3.length = extrusionAmount+hypLength; | |
sv3.angle = Angle.radFromDeg(extrusionAngle); | |
VMath.reverse(sv3); | |
v3 = VMath.add(v2,sv3); | |
// point FOUR | |
sv4 = new Vector2D(); | |
sv4.length = rect.height+oppLength; | |
sv4.angle = Angle.radFromDeg(270); | |
v4 = VMath.add(v3,sv4); | |
// point FIVE | |
sv5 = new Vector2D(); | |
sv5.length = extrusionAmount; | |
sv5.angle = Angle.radFromDeg(extrusionAngle); | |
v5 = VMath.add(v4,sv5); | |
break; | |
//------------------- | |
default : | |
throw new Error("Incorrect quadrant calculated. Check the angle logic."); | |
break; | |
} | |
} | |
} | |
]]> | |
</fx:Script> | |
<!-- _______________________________________________ --> | |
<s:Rect | |
id="rect" | |
width="{label.width+(labelPadding*2)}" | |
height="{label.height+(labelPadding*2)}" | |
> | |
<s:fill> | |
<s:SolidColor color="{label.getStyle('backgroundColor')}" /> | |
</s:fill> | |
</s:Rect> | |
<s:Label | |
id="label" | |
x="{labelPadding}" | |
y="{labelPadding}" | |
text="{text}" | |
maxWidth="{maxLabelWidth? maxLabelWidth : this.width }" | |
styleName="{labelStyleName}" | |
/> | |
<!-- __________________________________________________ --> | |
<s:Graphic> | |
<s:Path | |
id="extrusionPath" | |
data=" | |
M {vo.x} {vo.y} | |
L {v1.x} {v1.y} | |
L {v2.x} {v2.y} | |
L {v3.x} {v3.y} | |
L {v4.x} {v4.y} | |
L {v5.x} {v5.y} | |
" | |
blendMode="{pathBlendMode}" | |
alpha="{pathAlpha}" | |
> | |
<s:fill> | |
<s:SolidColor color="#ffffff" /> | |
</s:fill> | |
</s:Path> | |
</s:Graphic> | |
<!-- __________________________________________________ --> | |
<s:Graphic> | |
<s:Path | |
id="extrusionGreySidePath" | |
visible="false" | |
data=" | |
M {vo.x} {vo.y} | |
L {v1.x} {v1.y} | |
L {v2.x} {v2.y} | |
L {v3.x} {v3.y} | |
" | |
> | |
<s:fill> | |
<s:SolidColor color="#999999" /> | |
</s:fill> | |
</s:Path> | |
</s:Graphic> | |
<!-- __________________________________________________ --> | |
<s:Group | |
includeInLayout="false" | |
visible="false" | |
> | |
<s:Ellipse | |
id="dot1" | |
x="{v1.x-5}" | |
y="{v1.y-5}" | |
width="10" | |
height="10" | |
> | |
<s:fill> | |
<s:SolidColor color="#ff0000" /> | |
</s:fill> | |
</s:Ellipse> | |
<s:Label text="1" x="{dot1.x+8}" y="{dot1.y}" color="#ffffff" /> | |
<s:Ellipse | |
x="{v2.x-5}" | |
y="{v2.y-5}" | |
width="10" | |
height="10" | |
> | |
<s:fill> | |
<s:SolidColor color="#ff0000" /> | |
</s:fill> | |
</s:Ellipse> | |
<s:Label text="2" x="{v2.x+8}" y="{v2.y}" color="#ffffff" /> | |
<s:Ellipse | |
x="{v3.x-5}" | |
y="{v3.y-5}" | |
width="10" | |
height="10" | |
> | |
<s:fill> | |
<s:SolidColor color="#ff0000" /> | |
</s:fill> | |
</s:Ellipse> | |
<s:Label text="3" x="{v3.x+8}" y="{v3.y}" color="#ffffff" /> | |
<s:Ellipse | |
x="{v4.x-5}" | |
y="{v4.y-5}" | |
width="10" | |
height="10" | |
> | |
<s:fill> | |
<s:SolidColor color="#ff0000" /> | |
</s:fill> | |
</s:Ellipse> | |
<s:Label text="4" x="{v4.x+8}" y="{v4.y}" color="#ffffff" /> | |
<s:Ellipse | |
x="{v5.x-5}" | |
y="{v5.y-5}" | |
width="10" | |
height="10" | |
> | |
<s:fill> | |
<s:SolidColor color="#ff0000" /> | |
</s:fill> | |
</s:Ellipse> | |
<s:Label text="5" x="{v5.x+8}" y="{v5.y}" color="#ffffff" /> | |
</s:Group> | |
</s:Group> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.cc.layout | |
{ | |
import com.cc.components.TextBlockExtruded; | |
import mx.collections.ArrayCollection; | |
import mx.core.ILayoutElement; | |
import mx.core.IVisualElement; | |
import spark.components.supportClasses.GroupBase; | |
import spark.layouts.supportClasses.LayoutBase; | |
public class TextBlockExtrudedVerticalLayout extends LayoutBase | |
{ | |
protected var _quadrantNumber:int = 1; // the default angle is 290 so we are in Q4 | |
[Bindable] | |
public function get quadrantNumber():int { return _quadrantNumber; }; | |
public function set quadrantNumber(value:int):void { _quadrantNumber = value; }; | |
protected var _gap:Number = 10; | |
[Bindable] | |
public function get gap():Number { return _gap; }; | |
public function set gap(value:Number):void { _gap = value; }; | |
override public function updateDisplayList(width:Number, height:Number):void | |
{ | |
var layoutTarget:GroupBase = target; | |
var i:int; | |
var count:int = layoutTarget.numElements; | |
var element:ILayoutElement; | |
var elementWidth:Number; | |
var elementHeight:Number; | |
if(count>0) | |
{ | |
element = layoutTarget.getElementAt(0); | |
if(element is TextBlockExtruded) | |
{ | |
quadrantNumber = TextBlockExtruded(element).quadrantNumber; | |
} | |
else throw new Error("This layout has non-TextBlockExtruded instances in it! Boo!"); | |
for(i = 0; i<count; i++) | |
{ | |
element = layoutTarget.getElementAt(i); | |
element.setLayoutBoundsSize(NaN,NaN); | |
// check it is a TextBlockExtruded instance | |
if(element is TextBlockExtruded) | |
{ | |
elementHeight = TextBlockExtruded(element).rect.height; | |
} | |
else throw new Error("This layout has non-TextBlockExtruded instances in it! Boo!"); | |
var g:Number = i==0? 0 : gap; | |
element.setLayoutBoundsPosition(0,i*(elementHeight+g)); | |
} | |
// swap depths if in quadrant 1 or 2 | |
if(quadrantNumber == 1 || quadrantNumber == 2) | |
{ | |
i = 0; | |
while(i<count) | |
{ | |
// get the top element | |
element = layoutTarget.getElementAt(i) as TextBlockExtruded; | |
if (element is IVisualElement) | |
IVisualElement(element).depth = Math.abs(i-(count-1)); | |
i++; | |
} | |
} | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8"?> | |
<s:Application | |
xmlns:fx="http://ns.adobe.com/mxml/2009" | |
xmlns:s="library://ns.adobe.com/flex/spark" | |
xmlns:mx="library://ns.adobe.com/flex/mx" | |
xmlns:components="com.cc.components.*" | |
xmlns:layout="com.cc.layout.*" | |
minWidth="955" | |
minHeight="600" | |
initialize="application1_initializeHandler(event)" | |
creationComplete="application1_creationCompleteHandler(event)" | |
backgroundColor="#ffffff" | |
> | |
<fx:Script> | |
<![CDATA[ | |
import com.cc.components.TextBlockExtruded; | |
import flash.text.engine.FontLookup; | |
import flash.text.engine.RenderingMode; | |
import flash.text.engine.TextLine; | |
import flash.text.engine.TypographicCase; | |
import flashx.textLayout.compose.TextFlowLine; | |
import flashx.textLayout.container.ContainerController; | |
import flashx.textLayout.conversion.TextConverter; | |
import flashx.textLayout.elements.Configuration; | |
import flashx.textLayout.elements.OverflowPolicy; | |
import flashx.textLayout.elements.ParagraphElement; | |
import flashx.textLayout.elements.SpanElement; | |
import flashx.textLayout.elements.TextFlow; | |
import flashx.textLayout.events.CompositionCompleteEvent; | |
import flashx.textLayout.events.FlowOperationEvent; | |
import flashx.textLayout.events.UpdateCompleteEvent; | |
import flashx.textLayout.factory.StringTextLineFactory; | |
import flashx.textLayout.formats.TextAlign; | |
import flashx.textLayout.formats.TextDecoration; | |
import flashx.textLayout.formats.TextLayoutFormat; | |
import mx.collections.ArrayCollection; | |
import mx.core.IVisualElement; | |
import mx.events.FlexEvent; | |
import spark.events.TextOperationEvent; | |
import spark.utils.TextFlowUtil; | |
protected var config:Configuration; | |
protected var defaultFormat:TextLayoutFormat; | |
protected var elementCollection:ArrayCollection; | |
protected function application1_initializeHandler(event:FlexEvent):void | |
{ | |
config = Configuration(TextFlow.defaultConfiguration).clone(); | |
defaultFormat = new TextLayoutFormat(); | |
defaultFormat.paddingTop = 6; | |
defaultFormat.color = 0x666666; | |
defaultFormat.textAlign = TextAlign.LEFT; | |
defaultFormat.textAlignLast = TextAlign.LEFT; | |
defaultFormat.fontSize = 32; | |
defaultFormat.lineHeight = 38; | |
config.textFlowInitialFormat = defaultFormat; | |
textFlow.format = defaultFormat; | |
} | |
//------------------------------------------------- | |
protected function compositionCompleteHandler(event:CompositionCompleteEvent):void | |
{ | |
var eTextFlow:TextFlow = event.textFlow; | |
if(eTextFlow.flowComposer) | |
{ | |
if(!elementCollection) elementCollection = new ArrayCollection(); | |
elementCollection.removeAll(); | |
var i:int = 0; | |
var j:int = eTextFlow.flowComposer.numLines; | |
if(j>0) | |
{ | |
for(i = 0; i<j; i++) | |
{ | |
var textLine:TextFlowLine = eTextFlow.flowComposer.getLineAt(i); | |
var tf:TextFlow = textLine.paragraph.getTextFlow(); | |
var textOfLine:String = tf.getText(textLine.absoluteStart, textLine.absoluteStart + textLine.textLength); | |
//trace(textOfLine); | |
if (textLine) | |
{ | |
var rx:int = textLine.getBounds().x; | |
var ry:int = textLine.getBounds().y; | |
var rw:int = textLine.getBounds().width; | |
var rh:int = textLine.getBounds().height; | |
} | |
//trace("rx:"+rx+" /ry:"+ry+" /rw:"+rw+" /rh:"+rh); | |
addTextLine(textOfLine); | |
} | |
} | |
buildLayout(); | |
} | |
} | |
//------------------------------------------------- | |
protected function addTextLine(textOfLine:String):void | |
{ | |
var extrudedText:TextBlockExtruded = new TextBlockExtruded(); | |
extrudedText.text = textOfLine; | |
extrudedText.extrusionAmount = extrusionAmountSlider.value; | |
extrudedText.extrusionAngle = extrusionAngleSlider.value; | |
extrudedText.maxLabelWidth = 350; | |
elementCollection.addItem(extrudedText); | |
} | |
//------------------------------------------------- | |
protected function slider_changeHandler(event:Event):void | |
{ | |
buildLayout(); | |
} | |
//------------------------------------------------- | |
protected function buildLayout():void | |
{ | |
extrudedTextGroup.removeAllElements(); | |
var i:int = 0; | |
var j:int = elementCollection.length; | |
for(i;i<j;i++) | |
{ | |
var e:TextBlockExtruded = elementCollection.getItemAt(i) as TextBlockExtruded; | |
e.depth = i; | |
e.extrusionAngle = extrusionAngleSlider.value; | |
e.extrusionAmount = extrusionAmountSlider.value; | |
e.pathBlendMode = eraseModeBtn.selected? BlendMode.ERASE : BlendMode.NORMAL; | |
e.pathAlpha = extrusionAlphaSlider.value; | |
e.drawExtrusion(); | |
extrudedTextGroup.addElement(e); | |
} | |
} | |
//------------------------------------------------- | |
protected function textInputChangeBtn_clickHandler(event:MouseEvent):void | |
{ | |
textFlow.removeEventListener(CompositionCompleteEvent.COMPOSITION_COMPLETE,compositionCompleteHandler); | |
var tf:TextFlow = TextConverter.importToFlow(inputText.text, TextConverter.PLAIN_TEXT_FORMAT); | |
tf.addEventListener(CompositionCompleteEvent.COMPOSITION_COMPLETE,compositionCompleteHandler); | |
textFlowView.textFlow = tf; | |
textFlowView.textFlow.format = defaultFormat; | |
} | |
]]> | |
</fx:Script> | |
<s:BitmapImage | |
source="@Embed(source='assets/images/lastminute_bg.jpg')" | |
width="100%" | |
height="100%" | |
alpha="0.5" | |
/> | |
<s:Group | |
id="extrusionGroupContainer" | |
width="100%" | |
height="100%" | |
blendMode="layer" | |
> | |
<s:BitmapImage | |
source="@Embed(source='assets/images/lastminute_bg.jpg')" | |
width="100%" | |
height="100%" | |
/> | |
<s:Group | |
id="extrudedTextGroup" | |
width="100%" | |
height="100%" | |
top="100" | |
left="260" | |
> | |
<s:layout> | |
<layout:TextBlockExtrudedVerticalLayout /> | |
</s:layout> | |
</s:Group> | |
</s:Group> | |
<s:Panel | |
id="panel" | |
width="100%" | |
height="240" | |
left="20" | |
right="20" | |
bottom="20" | |
title="EXTRUDED TEXT WORKSHOP" | |
> | |
<s:layout> | |
<s:VerticalLayout | |
gap="80" | |
paddingBottom="20" | |
paddingLeft="20" | |
paddingRight="20" | |
paddingTop="20" | |
/> | |
</s:layout> | |
<s:Group> | |
<s:HGroup | |
gap="20" | |
width="100%" | |
height="100%" | |
> | |
<s:VGroup> | |
<s:TextInput | |
id="inputText" | |
text="Feel free to change the text. This will create extruded text blocks. Line breaks are determined by the TextFlow area." | |
maxChars="250" | |
width="350" | |
/> | |
<s:Button | |
id="textInputChangeBtn" | |
label="CHANGE TEXT" | |
click="textInputChangeBtn_clickHandler(event)" | |
enabled="{inputText.text!=''}" | |
/> | |
<s:HGroup | |
id="controls" | |
paddingTop="40" | |
> | |
<s:VGroup> | |
<s:Label text="EXTRUSION ANGLE" /> | |
<s:HSlider | |
id="extrusionAngleSlider" | |
stepSize="1" | |
minimum="0" | |
maximum="360" | |
value="290" | |
change="slider_changeHandler(event)" | |
/> | |
</s:VGroup> | |
<s:VGroup | |
height="100%" | |
> | |
<s:Label text="EXTRUSION AMOUNT" /> | |
<s:HSlider | |
id="extrusionAmountSlider" | |
stepSize="1" | |
minimum="10" | |
maximum="1000" | |
value="500" | |
change="slider_changeHandler(event)" | |
/> | |
</s:VGroup> | |
<s:VGroup | |
height="100%" | |
> | |
<s:Label text="EXTRUSION ALPHA" /> | |
<s:HSlider | |
id="extrusionAlphaSlider" | |
stepSize="0.01" | |
minimum="0" | |
maximum="1" | |
value="0.50" | |
change="slider_changeHandler(event)" | |
/> | |
</s:VGroup> | |
<s:VGroup | |
height="100%" | |
> | |
<s:Label text="ERASE MODE" /> | |
<s:ToggleButton | |
id="eraseModeBtn" | |
label="{eraseModeBtn.selected? 'ON' : 'OFF'}" | |
click="slider_changeHandler(event)" | |
/> | |
</s:VGroup> | |
</s:HGroup> | |
</s:VGroup> | |
<s:TextArea | |
id="textFlowView" | |
width="350" | |
height="150" | |
skinClass="com.cc.skins.BlankTextAreaSkin" | |
> | |
<s:textFlow> | |
<s:TextFlow | |
id="textFlow" | |
compositionComplete="compositionCompleteHandler(event)" | |
> | |
<s:p>TOP SECRET</s:p> | |
<s:p>Hey lastminute_com just back from Rome ... casual #prouding pose at the colosseum</s:p> | |
<s:p>#PROUDING</s:p> | |
</s:TextFlow> | |
</s:textFlow> | |
</s:TextArea> | |
</s:HGroup> | |
</s:Group> | |
</s:Panel> | |
</s:Application> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.cc.math | |
{ | |
/** | |
* A basic 2-dimensional vector class. | |
*/ | |
public class Vector2D | |
{ | |
[Bindable] | |
public var x:Number; | |
[Bindable] | |
public var y:Number; | |
public var isNormalized:Boolean; | |
public function Vector2D(_x:Number = 0, _y:Number = 0) | |
{ | |
x = _x; | |
y = _y; | |
} | |
/** | |
* Generates a copy of this vector. | |
* @return Vector2D A copy of this vector. | |
*/ | |
public function clone():Vector2D | |
{ | |
return new Vector2D(x, y); | |
} | |
/** | |
* Whether or not this vector is equal to zero, i.e. its x, y, and length are zero. | |
* @return Boolean True if vector is zero, otherwise false. | |
*/ | |
public function isZero():Boolean | |
{ | |
return x == 0 && y == 0; | |
} | |
public function set length(value:Number):void | |
{ | |
var a:Number = angle; | |
x = Math.cos(a) * value; | |
y = Math.sin(a) * value; | |
} | |
public function get length():Number | |
{ | |
return Math.sqrt(x * x + y * y); | |
} | |
public function set angle(value:Number):void | |
{ | |
var len:Number = length; | |
x = Math.cos(value) * len; | |
y = Math.sin(value) * len; | |
} | |
public function get angle():Number { | |
return Math.atan2(y, x); | |
} | |
/** | |
* Calculates the dot product of this vector and another given vector. | |
* @param v2 Another Vector2D instance. | |
* @return Number The dot product of this vector and the one passed in as a parameter. | |
*/ | |
public function dotProd(v2:Vector2D):Number { | |
return x * v2.x + y * v2.y; | |
} | |
/** | |
* Calculates the cross product of this vector and another given vector. | |
* @param v2 Another Vector2D instance. | |
* @return Number The cross product of this vector and the one passed in as a parameter. | |
*/ | |
public function crossProd(v2:Vector2D):Number | |
{ | |
return x * v2.y - y * v2.x; | |
} | |
/** | |
* Calculates the angle between two vectors. | |
* @param v1 The first Vector2D instance. | |
* @param v2 The second Vector2D instance. | |
* @return Number the angle between the two given vectors. | |
*/ | |
public static function angleBetween(v1:Vector2D, v2:Vector2D):Number | |
{ | |
if(!v1.isNormalized) v1 = v1.clone().normalize(); | |
if(!v2.isNormalized) v2 = v2.clone().normalize(); | |
return Math.acos(v1.dotProd(v2)); | |
} | |
/** | |
* Determines if a given vector is to the right or left of this vector. | |
* @return int If to the left, returns -1. If to the right, +1. | |
*/ | |
public function sign(v2:Vector2D):int | |
{ | |
return perp.dotProd(v2) < 0 ? -1 : 1; | |
} | |
/** | |
* Finds a vector that is perpendicular to this vector. | |
* @return Vector2D A vector that is perpendicular to this vector. | |
*/ | |
public function get perp():Vector2D | |
{ | |
return new Vector2D(-y, x); | |
} | |
/** | |
* Calculates the distance from this vector to another given vector. | |
* @param v2 A Vector2D instance. | |
* @return Number The distance from this vector to the vector passed as a parameter. | |
*/ | |
public function dist(v2:Vector2D):Number { | |
return Math.sqrt(distSQ(v2)); | |
} | |
/** | |
* Calculates the distance squared from this vector to another given vector. | |
* @param v2 A Vector2D instance. | |
* @return Number The distance squared from this vector to the vector passed as a parameter. | |
*/ | |
public function distSQ(v2:Vector2D):Number | |
{ | |
var dx:Number = v2.x - x; | |
var dy:Number = v2.y - y; | |
return dx * dx + dy * dy; | |
} | |
/** | |
* Brings the given vector's magnitude down to a 0-1 value so it can be used to constrain other vectors. | |
*/ | |
public function normalize():Vector2D | |
{ | |
var vx:Number = x; | |
var vy:Number = y; | |
var len:Number = Math.sqrt((vx * vx) + (vy * vy)); | |
if (len) | |
{ | |
x /= len; | |
y /= len; | |
} | |
isNormalized = true; | |
return this; | |
} | |
/** | |
* Returns a String description of the Vector2D object. | |
*/ | |
public function toString():String | |
{ | |
return "x:"+x.toFixed(2)+" / y:"+y.toFixed(2)+" / length: "+length.toFixed(2)+" / angle: "+angle.toFixed(2); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.cc.math | |
{ | |
/** | |
* Static class that has some of the vector math functions for behaviors. | |
*/ | |
public class VMath { | |
/** | |
* Calculates the distance squared from first vector to second vector. | |
* @param v1 The Vector2D in question | |
* @param v2 The Vector2D to be added to v1 | |
*/ | |
static public function add(v1:Vector2D, v2:Vector2D):Vector2D | |
{ | |
var v:Vector2D = new Vector2D(); | |
v.x = v1.x + v2.x; | |
v.y = v1.y + v2.y; | |
return v; | |
} | |
/** | |
* Calculates the distance squared from first vector to second vector. | |
*/ | |
static public function subtract(v1:Vector2D, v2:Vector2D):Vector2D | |
{ | |
var v:Vector2D = new Vector2D(); | |
v.x = v1.x - v2.x; | |
v.y = v1.y - v2.y; | |
return v; | |
} | |
/** | |
* Multiplies the two vectors by a value, creating a new Vector2D instance to hold the result. | |
* @param v2 A Vector2D instance. | |
* @param value The value to multiply the vector instance by. | |
*/ | |
static public function multiply(v1:Vector2D, value:Number):Vector2D | |
{ | |
var v:Vector2D = new Vector2D(); | |
v.x = v1.x * value; | |
v.y = v1.y * value; | |
return v; | |
} | |
/** | |
* Divides this vector by a value, creating a new Vector2D instance to hold the result. | |
* @param v2 A Vector2D instance. | |
* @param value The value to multiply the vector instance by. | |
*/ | |
static public function divide(v1:Vector2D, value:Number):Vector2D | |
{ | |
var v:Vector2D = new Vector2D(); | |
v.x = v1.x / value; | |
v.y = v1.y / value; | |
return v; | |
} | |
/** | |
* Brings the given vector's magnitude down to a 0-1 value so it can be used to constrain other vectors. | |
* @param v A Vector2D instance. | |
*/ | |
static public function normalize(v:Vector2D):void | |
{ | |
var vx:Number = v.x; | |
var vy:Number = v.y; | |
var len:Number = Math.sqrt((vx * vx) + (vy * vy)); | |
if (len) | |
{ | |
v.x /= len; | |
v.y /= len; | |
} | |
v.isNormalized = true; | |
} | |
/** | |
* Ensures the length of the vector is no longer than the given value. | |
* @param v A Vector2D instance to truncate. | |
* @param max The maximum value this vector should be. If length is larger than max, it will be truncated to this value. | |
*/ | |
static public function truncate(v:Vector2D, max:Number):void { | |
var vx:Number = v.x; | |
var vy:Number = v.y; | |
var cl:Number = Math.sqrt((vx * vx) + (vy * vy)); | |
var nl:Number = Math.min(max, cl); | |
var a:Number = Math.atan2(vy, vx); | |
v.x = Math.cos(a) * nl; | |
v.y = Math.sin(a) * nl; | |
} | |
static public function reverse(v:Vector2D):void { | |
v.x = -v.x; | |
v.y = -v.y; | |
} | |
} // end class | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment