Skip to content

Instantly share code, notes, and snippets.

@jpsarda
Last active December 11, 2023 04:17
Show Gist options
  • Save jpsarda/4573831 to your computer and use it in GitHub Desktop.
Save jpsarda/4573831 to your computer and use it in GitHub Desktop.
A class to draw lines with Futile, 2D engine for Unity3D. API inspired from Flash AS3 drawing API. Works with transparent colors. Cap styles : NONE, ROUND, SQUARE, TRIANGLE, ARROW Joint styles : MITER, ROUND, BEVEL Experimental (but working for most usages) support of borders (so far only supported with styles NONE / BEVEL).
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
/* Author @jpsarda
* A drawing class.
*
* Examples :
*
_draw=new FDrawingSprite("Blank");
AddChild(_draw);
_draw.SetLineThickness(10);
_draw.SetLineColor(new Color(1,0,1,0.5f));
_draw.SetLineCapStyle(FTDrawingCapStyle.ARROW);
_draw.SetLineJointStyle(FTDrawingJointStyle.ROUND);
_draw.MoveTo(50,50);
_draw.LineTo(100,100);
_draw.LineTo(120,80);
_draw.LineTo(120,50);
_draw.LineTo(170,50);
_draw.Flush(); // Stop the line, add caps
_draw.SetLineColor(new Color(0.5f,0.5f,1.0f,1.0f));
_draw.SetLineCapStyle(FTDrawingCapStyle.ROUND);
_draw.SetLineJointStyle(FTDrawingJointStyle.BEVEL);
_draw.ClearBorders();
_draw.MoveTo(-50,50);
_draw.LineTo(-100,100);
_draw.LineTo(-120,80);
_draw.LineTo(-120,50);
_draw.LineTo(-170,50);
_draw.Flush();
_draw.ClearBorders();
_draw.PushBorder(5.0f,new Color(1.0f,0.5f,0.0f,1.0f),false);
_draw.PushBorder(10.0f,new Color(0.0f,0.0f,0.0f,0.25f),true);
_draw.SetLineColor(new Color(1,1,0,1.0f));
_draw.MoveTo(40,0);
for (int i=1;i<16;i++) {
float angle=-2*Mathf.PI*(float)i/16.0f;
_draw.LineTo (Mathf.Cos (angle)*40.0f,Mathf.Sin (angle)*40.0f);
}
_draw.Loop(); //Stop the line and loop with first line
*
*
*/
public enum FTDrawingCapStyle
{
NONE,
ROUND,
SQUARE,
TRIANGLE,
ARROW
}
public enum FTDrawingJointStyle
{
MITER,
ROUND,
BEVEL
}
public class FDrawingBorder
{
public Color color;
public float thickness;
public bool gradient;
public bool top,bottom;
public FDrawingQuad topQuad,bottomQuad;
public FDrawingBorder Clone() {
return new FDrawingBorder(this);
}
public FDrawingBorder(float thickness,Color color, bool gradient, bool top, bool bottom):base()
{
this.thickness=thickness;
this.gradient=gradient;
this.color=color;
this.top=top;
this.bottom=bottom;
topQuad=bottomQuad=null;
}
public FDrawingBorder(FDrawingBorder model):base()
{
Copy(model);
}
public void Copy(FDrawingBorder model) {
thickness=model.thickness;
gradient=model.gradient;
color=model.color;
top=model.top;
bottom=model.bottom;
topQuad=model.topQuad;
bottomQuad=model.bottomQuad;
}
}
public class FDrawingCursor
{
public Vector2 position,lineFromPosition,direction;
public bool lineFromValid;
public Color color;
public float thickness;
public FTDrawingJointStyle jointType;
public FTDrawingCapStyle capType;
protected LinkedList<FDrawingQuad> _quads;
//public FDrawingQuad topQuad,bottomQuad;
public FDrawingQuad lineQuad;
public List<FDrawingBorder> borders;
public FDrawingCursor Clone() {
return new FDrawingCursor(this);
}
public FDrawingCursor(LinkedList<FDrawingQuad> quads):base()
{
_quads=quads;
lineFromValid=false;
thickness=1.0f;
jointType=FTDrawingJointStyle.BEVEL;
capType=FTDrawingCapStyle.NONE;
color=new Color(1,1,1,1);
position=new Vector2(0,0);
//topQuad=bottomQuad=null;
lineQuad=null;
borders=null;
}
public FDrawingCursor(FDrawingCursor model):base()
{
Copy(model);
}
public void Copy(FDrawingCursor model) {
_quads=model.GetQuads();
lineFromValid=model.lineFromValid;
thickness=model.thickness;
jointType=model.jointType;
capType=model.capType;
color=model.color;
position=model.position;
lineFromPosition=model.lineFromPosition;
direction=model.direction;
//topQuad=model.topQuad;
//bottomQuad=model.bottomQuad;
lineQuad=model.lineQuad;
if (model.borders!=null) {
borders=new List<FDrawingBorder>(model.borders.Count);
int n=model.borders.Count;
for (int i=0;i<n;i++) {
FDrawingBorder border=model.borders[i];
borders.Add(border.Clone());
}
} else {
borders=null;
}
}
public LinkedList<FDrawingQuad> GetQuads() {
return _quads;
}
virtual public void MoveTo(float x,float y,FDrawingCursor firstCursor)
{
Flush (firstCursor);
position.x=x;
position.y=y;
}
virtual protected void AddLineSideBorders(FDrawingQuad topBaseQuad,FDrawingQuad bottomBaseQuad)
{
if (borders!=null) {
Vector2 ortho=new Vector2(-direction.y,direction.x);
FDrawingQuad quad;
int n=borders.Count;
for (int i=0;i<n;i++) {
FDrawingBorder border = borders[i];
//top
if (border.top) {
quad=new FDrawingQuad(border.color);
quad.tlVertice=topBaseQuad.tlVertice+ortho*border.thickness;
quad.trVertice=topBaseQuad.trVertice+ortho*border.thickness;
quad.blVertice=topBaseQuad.tlVertice;
quad.brVertice=topBaseQuad.trVertice;
if (border.gradient) {
quad.blColor=topBaseQuad.tlColor;
quad.brColor=topBaseQuad.trColor;
}
_quads.AddLast(quad);
border.topQuad=quad;
topBaseQuad=quad;
}
//bottom
if (border.bottom) {
quad=new FDrawingQuad(border.color);
quad.blVertice=bottomBaseQuad.blVertice-ortho*border.thickness;
quad.brVertice=bottomBaseQuad.brVertice-ortho*border.thickness;
quad.tlVertice=bottomBaseQuad.blVertice;
quad.trVertice=bottomBaseQuad.brVertice;
if (border.gradient) {
quad.tlColor=bottomBaseQuad.blColor;
quad.trColor=bottomBaseQuad.brColor;
}
_quads.AddLast(quad);
border.bottomQuad=quad;
bottomBaseQuad=quad;
}
}
}
}
virtual public bool LineTo(float x,float y,FDrawingCursor previousCursor) {
//Main line (no caps, caps are drawn on Flush, MoveTo)
FDrawingQuad quad;
quad=new FDrawingQuad(color);
if (!quad.SetLineVertices(position,new Vector2(x,y),thickness,this)) {
return false;
}
AddLineSideBorders(quad,quad);
_quads.AddLast(quad);
lineQuad=quad;
lineFromValid=true;
lineFromPosition=position;
position.x=x;
position.y=y;
if (previousCursor!=null) {
DrawJoint(previousCursor);
}
return true;
}
virtual public void Flush(FDrawingCursor firstCursor)
{
if (lineFromValid) {
DrawEndCap();
}
if (firstCursor!=null) {
firstCursor.DrawStartCap();
}
lineFromValid=false;
}
virtual public void Loop(FDrawingCursor firstCursor,FDrawingCursor previousCursor)
{
if (firstCursor!=null) {
LineTo (firstCursor.lineFromPosition.x,firstCursor.lineFromPosition.y,previousCursor);
firstCursor.DrawJoint(this);
lineFromValid=false;
} else {
Flush (firstCursor);
}
}
protected static string supportedCapTypesWithBorders="Supported cap styles : FTDrawingCapStyle.NONE";
protected static string supportedJointTypesWithBorders="Supported joint styles : FTDrawingJointStyle.BEVEL";
virtual public void DrawEndCap() {
//Draw ending cap
if (capType==FTDrawingCapStyle.SQUARE) {
FDrawingQuad quad=new FDrawingQuad(color);
quad.SetLineVertices(position,position+direction*thickness*0.5f,thickness,null);
_quads.AddLast(quad);
if ((borders!=null)&&(borders.Count>0)) {
throw new FutileException("Cap type "+capType+" not suported with borders. "+supportedCapTypesWithBorders);
}
} else if (capType==FTDrawingCapStyle.TRIANGLE) {
FDrawingQuad quad=new FDrawingQuad(color);
Vector2 ortho=new Vector2(-direction.y,direction.x);
quad.tlVertice=position+ortho*thickness*0.5f;
quad.blVertice=position-ortho*thickness*0.5f;
quad.brVertice=position+direction*thickness*0.5f;
quad.trVertice=quad.brVertice;
_quads.AddLast(quad);
if ((borders!=null)&&(borders.Count>0)) {
throw new FutileException("Cap type "+capType+" not suported with borders. "+supportedCapTypesWithBorders);
}
} else if (capType==FTDrawingCapStyle.ARROW) {
FDrawingQuad quad;
Vector2 ortho=new Vector2(-direction.y,direction.x);
if (color.a>=1) {
quad=new FDrawingQuad(color);
quad.tlVertice=position+ortho*thickness*1.0f-direction*thickness*0.5f;
quad.blVertice=position-ortho*thickness*1.0f-direction*thickness*0.5f;
quad.brVertice=position+direction*thickness*0.5f;
quad.trVertice=quad.brVertice;
_quads.AddLast(quad);
} else {
Vector2 A=position+direction*thickness*0.5f;
quad=new FDrawingQuad(color);
quad.tlVertice=position+ortho*thickness*0.5f;
quad.blVertice=position+ortho*thickness*0.5f-direction*thickness*0.5f;
quad.brVertice=position+ortho*thickness*1.0f-direction*thickness*0.5f;
quad.trVertice=A;
_quads.AddLast(quad);
quad=new FDrawingQuad(color);
quad.tlVertice=position-ortho*thickness*0.5f;
quad.blVertice=position-ortho*thickness*0.5f-direction*thickness*0.5f;
quad.brVertice=position-ortho*thickness*1.0f-direction*thickness*0.5f;
quad.trVertice=A;
_quads.AddLast(quad);
quad=new FDrawingQuad(color);
quad.tlVertice=position+ortho*thickness*0.5f;
quad.blVertice=position-ortho*thickness*0.5f;
quad.brVertice=A;
quad.trVertice=A;
_quads.AddLast(quad);
}
if ((borders!=null)&&(borders.Count>0)) {
throw new FutileException("Cap type "+capType+" not suported with borders. "+supportedCapTypesWithBorders);
}
} else if (capType==FTDrawingCapStyle.ROUND) {
Vector2 ortho=new Vector2(-direction.y,direction.x);
int nbQuads=(int)(this.thickness*0.5f*Mathf.PI *0.5f *0.5f *0.5f)*2+2;
float angle=0;
//2 triangles by quads
float deltaAngle=0.5f*Mathf.PI/nbQuads;
for (int i=0;i<nbQuads;i++) {
FDrawingQuad quad=new FDrawingQuad(color);
quad.blVertice=position;
quad.tlVertice=position+ortho*thickness*0.5f*Mathf.Cos(angle)+direction*thickness*0.5f*Mathf.Sin(angle);
angle+=deltaAngle;
quad.trVertice=position+ortho*thickness*0.5f*Mathf.Cos(angle)+direction*thickness*0.5f*Mathf.Sin(angle);
angle+=deltaAngle;
quad.brVertice=position+ortho*thickness*0.5f*Mathf.Cos(angle)+direction*thickness*0.5f*Mathf.Sin(angle);
_quads.AddLast(quad);
}
if ((borders!=null)&&(borders.Count>0)) {
throw new FutileException("Cap type "+capType+" not suported with borders. "+supportedCapTypesWithBorders);
}
}
}
virtual public void DrawStartCap() {
//Draw starting cap
if (capType==FTDrawingCapStyle.SQUARE) {
FDrawingQuad quad=new FDrawingQuad(color);
quad.SetLineVertices(lineFromPosition-direction*thickness*0.5f,lineFromPosition,thickness,null);
_quads.AddLast(quad);
if ((borders!=null)&&(borders.Count>0)) {
throw new FutileException("Cap type "+capType+" not suported with borders. "+supportedCapTypesWithBorders);
}
} else if (capType==FTDrawingCapStyle.TRIANGLE) {
FDrawingQuad quad=new FDrawingQuad(color);
Vector2 ortho=new Vector2(-direction.y,direction.x);
quad.trVertice=lineFromPosition+ortho*thickness*0.5f;
quad.brVertice=lineFromPosition-ortho*thickness*0.5f;
quad.blVertice=lineFromPosition-direction*thickness*0.5f;
quad.tlVertice=lineFromPosition-direction*thickness*0.5f;
_quads.AddLast(quad);
if ((borders!=null)&&(borders.Count>0)) {
throw new FutileException("Cap type "+capType+" not suported with borders. "+supportedCapTypesWithBorders);
}
} else if (capType==FTDrawingCapStyle.ARROW) {
FDrawingQuad quad;
Vector2 ortho=new Vector2(-direction.y,direction.x);
if (color.a>=1) {
quad=new FDrawingQuad(color);
quad.trVertice=lineFromPosition+ortho*thickness*1.0f+direction*thickness*0.5f;
quad.brVertice=lineFromPosition-ortho*thickness*1.0f+direction*thickness*0.5f;;
quad.blVertice=lineFromPosition-direction*thickness*0.5f;
quad.tlVertice=lineFromPosition-direction*thickness*0.5f;
_quads.AddLast(quad);
} else {
Vector2 A=lineFromPosition-direction*thickness*0.5f;
quad=new FDrawingQuad(color);
quad.tlVertice=lineFromPosition+ortho*thickness*0.5f;
quad.blVertice=lineFromPosition+ortho*thickness*0.5f+direction*thickness*0.5f;
quad.brVertice=lineFromPosition+ortho*thickness*1.0f+direction*thickness*0.5f;
quad.trVertice=A;
_quads.AddLast(quad);
quad=new FDrawingQuad(color);
quad.tlVertice=lineFromPosition-ortho*thickness*0.5f;
quad.blVertice=lineFromPosition-ortho*thickness*0.5f+direction*thickness*0.5f;
quad.brVertice=lineFromPosition-ortho*thickness*1.0f+direction*thickness*0.5f;
quad.trVertice=A;
_quads.AddLast(quad);
quad=new FDrawingQuad(color);
quad.tlVertice=lineFromPosition+ortho*thickness*0.5f;
quad.blVertice=lineFromPosition-ortho*thickness*0.5f;
quad.brVertice=A;
quad.trVertice=A;
_quads.AddLast(quad);
}
if ((borders!=null)&&(borders.Count>0)) {
throw new FutileException("Cap type "+capType+" not suported with borders. "+supportedCapTypesWithBorders);
}
} else if (capType==FTDrawingCapStyle.ROUND) {
Vector2 ortho=new Vector2(-direction.y,direction.x);
int nbQuads=(int)(this.thickness*0.5f*Mathf.PI *0.5f *0.5f *0.5f)*2+2;
float angle=0;
//2 triangles by quads
float deltaAngle=0.5f*Mathf.PI/nbQuads;
for (int i=0;i<nbQuads;i++) {
FDrawingQuad quad=new FDrawingQuad(color);
quad.trVertice=lineFromPosition;
quad.brVertice=lineFromPosition-ortho*thickness*0.5f*Mathf.Cos(angle)-direction*thickness*0.5f*Mathf.Sin(angle);
angle+=deltaAngle;
quad.blVertice=lineFromPosition-ortho*thickness*0.5f*Mathf.Cos(angle)-direction*thickness*0.5f*Mathf.Sin(angle);
angle+=deltaAngle;
quad.tlVertice=lineFromPosition-ortho*thickness*0.5f*Mathf.Cos(angle)-direction*thickness*0.5f*Mathf.Sin(angle);
_quads.AddLast(quad);
}
if ((borders!=null)&&(borders.Count>0)) {
throw new FutileException("Cap type "+capType+" not suported with borders. "+supportedCapTypesWithBorders);
}
}
}
virtual protected void AddBorders (Vector2 leftPoint, Color leftColor, Vector2 leftDirectionNormalized, Vector2 rightPoint, Color rightColor, Vector2 rightDirectionNormalized, bool top) {
if ((borders!=null)&&(borders.Count>0)) {
int n=borders.Count;
for (int i=0;i<n;i++) {
FDrawingBorder border = borders[i];
if ((border.top && top)||(border.bottom && !top)) {
FDrawingQuad quad=new FDrawingQuad(border.color);
quad.tlVertice=leftPoint+leftDirectionNormalized*border.thickness;
quad.blVertice=leftPoint;
quad.brVertice=rightPoint;
quad.trVertice=rightPoint+rightDirectionNormalized*border.thickness;
if (border.gradient) {
quad.blColor=leftColor;
quad.brColor=rightColor;
}
_quads.AddLast(quad);
leftPoint=quad.tlVertice;
leftColor=quad.tlColor;
rightPoint=quad.trVertice;
rightColor=quad.trColor;
}
}
}
}
virtual public void DrawJoint(FDrawingCursor previousCursor) {
Vector2 prevOrtho=new Vector2(-previousCursor.direction.y,previousCursor.direction.x);
Vector2 ortho=new Vector2(-direction.y,direction.x);
float dot=Vector2.Dot(prevOrtho,direction);
//Cut inside of the turn
bool medianDone=false;
Vector2 median=Vector2.zero;
if (borders!=null) {
if (borders.Count>0) {
median=prevOrtho+ortho;
median.Normalize();
medianDone=true;
if (median==Vector2.zero) {
//???
} else {
Vector2 A=lineFromPosition;
Vector2 B=lineFromPosition+median;
float S,T;
bool ret;
if (dot<0) { //bottomQuad
int n=borders.Count;
for (int i=0;i<n;i++) {
FDrawingBorder border = borders[i];
if (border.bottom) {
FDrawingBorder previousBorder=previousCursor.borders[i];
//Previous line
//bottom line
ret=VectorUtils.LinesIntersect(A,B,previousBorder.bottomQuad.blVertice,previousBorder.bottomQuad.brVertice,out S,out T);
if ((ret)&&(T>=0)&&(T<=1)) {
previousBorder.bottomQuad.brVertice = A + S * (B - A);
} else {
ret=VectorUtils.LinesIntersect(A,B,previousBorder.bottomQuad.tlVertice,previousBorder.bottomQuad.blVertice,out S,out T);
if ((ret)&&(T>=0)&&(T<=1)) {
previousBorder.bottomQuad.brVertice = A + S * (B - A);
previousBorder.bottomQuad.blVertice = previousBorder.bottomQuad.brVertice;
}
}
//topline
ret=VectorUtils.LinesIntersect(A,B,previousBorder.bottomQuad.tlVertice,previousBorder.bottomQuad.trVertice,out S,out T);
if ((ret)&&(T>=0)&&(T<=1)) {
previousBorder.bottomQuad.trVertice = A + S * (B - A);
}
//Current line
//bottom line
ret=VectorUtils.LinesIntersect(A,B,border.bottomQuad.blVertice,border.bottomQuad.brVertice,out S,out T);
if ((ret)&&(T>=0)&&(T<=1)) {
border.bottomQuad.blVertice = A + S * (B - A);
} else {
ret=VectorUtils.LinesIntersect(A,B,border.bottomQuad.trVertice,border.bottomQuad.brVertice,out S,out T);
if ((ret)&&(T>=0)&&(T<=1)) {
border.bottomQuad.brVertice = A + S * (B - A);
border.bottomQuad.blVertice = border.bottomQuad.brVertice;
}
}
//topline
ret=VectorUtils.LinesIntersect(A,B,border.bottomQuad.tlVertice,border.bottomQuad.trVertice,out S,out T);
if ((ret)&&(T>=0)&&(T<=1)) {
border.bottomQuad.tlVertice = A + S * (B - A);
}
}
//i++;
}
} else if (dot>0) { //topQuad
int n=borders.Count;
for (int i=0;i<n;i++) {
FDrawingBorder border = borders[i];
if (border.top) {
FDrawingBorder previousBorder=previousCursor.borders[i];
//Previous line
//top line
ret=VectorUtils.LinesIntersect(A,B,previousBorder.topQuad.tlVertice,previousBorder.topQuad.trVertice,out S,out T);
if ((ret)&&(T>=0)&&(T<=1)) {
previousBorder.topQuad.trVertice = A + S * (B - A);
} else {
ret=VectorUtils.LinesIntersect(A,B,previousBorder.topQuad.tlVertice,previousBorder.topQuad.blVertice,out S,out T);
if ((ret)&&(T>=0)&&(T<=1)) {
previousBorder.topQuad.trVertice = A + S * (B - A);
previousBorder.topQuad.tlVertice = previousBorder.topQuad.trVertice;
}
}
//bottom line
ret=VectorUtils.LinesIntersect(A,B,previousBorder.topQuad.blVertice,previousBorder.topQuad.brVertice,out S,out T);
if ((ret)&&(T>=0)&&(T<=1)) {
previousBorder.topQuad.brVertice = A + S * (B - A);
}
//Current line
//top line
ret=VectorUtils.LinesIntersect(A,B,border.topQuad.tlVertice,border.topQuad.trVertice,out S,out T);
if ((ret)&&(T>=0)&&(T<=1)) {
border.topQuad.tlVertice = A + S * (B - A);
} else {
ret=VectorUtils.LinesIntersect(A,B,border.topQuad.trVertice,border.topQuad.trVertice,out S,out T);
if ((ret)&&(T>=0)&&(T<=1)) {
border.topQuad.trVertice = A + S * (B - A);
border.topQuad.tlVertice = border.topQuad.trVertice;
}
}
//bottom line
ret=VectorUtils.LinesIntersect(A,B,border.topQuad.blVertice,border.topQuad.brVertice,out S,out T);
if ((ret)&&(T>=0)&&(T<=1)) {
border.topQuad.blVertice = A + S * (B - A);
}
}
//i++;
}
}
}
}
}
Vector2 center=lineFromPosition;
//if ((color.a<1)&&(previousCursor.color.a<1)) { //not necessary for solid colors but removing overlapping mught help performances anyway
if (!medianDone) {
median=prevOrtho+ortho;
median.Normalize();
medianDone=true;
}
if (median==Vector2.zero) {
//???
} else {
Vector2 A=lineFromPosition;
Vector2 B=lineFromPosition+median;
float S,T;
bool ret;
if (dot<0) { //bottomQuad
//Previous line
ret=VectorUtils.LinesIntersect(A,B,previousCursor.lineQuad.blVertice,previousCursor.lineQuad.brVertice,out S,out T);
if ((ret)&&(T>=0)&&(T<=1)) {
center=previousCursor.lineQuad.brVertice = A + S * (B - A);
}
//Current line
ret=VectorUtils.LinesIntersect(A,B,lineQuad.blVertice,lineQuad.brVertice,out S,out T);
if ((ret)&&(T>=0)&&(T<=1)) {
center=lineQuad.blVertice = A + S * (B - A);
}
} else if (dot>0) { //topQuad
//Previous line
ret=VectorUtils.LinesIntersect(A,B,previousCursor.lineQuad.tlVertice,previousCursor.lineQuad.trVertice,out S,out T);
if ((ret)&&(T>=0)&&(T<=1)) {
center=previousCursor.lineQuad.trVertice = A + S * (B - A);
}
//Current line
ret=VectorUtils.LinesIntersect(A,B,lineQuad.tlVertice,lineQuad.trVertice,out S,out T);
if ((ret)&&(T>=0)&&(T<=1)) {
center=lineQuad.tlVertice = A + S * (B - A);
}
}
}
//}
//Draw joint
if (jointType==FTDrawingJointStyle.BEVEL) {
if (dot<0) {
FDrawingQuad quad=new FDrawingQuad(this.color);
quad.tlVertice=previousCursor.position+prevOrtho*previousCursor.thickness*0.5f;
quad.blVertice=center;
quad.brVertice=center;
quad.trVertice=lineFromPosition+ortho*thickness*0.5f;
_quads.AddLast(quad);
AddBorders (quad.tlVertice,quad.tlColor,prevOrtho,quad.trVertice,quad.trColor,ortho,true);
} else if (dot>0) {
FDrawingQuad quad=new FDrawingQuad(this.color);
quad.blVertice=previousCursor.position-prevOrtho*previousCursor.thickness*0.5f;
quad.tlVertice=center;
quad.trVertice=center;
quad.brVertice=lineFromPosition-ortho*thickness*0.5f;
_quads.AddLast(quad);
AddBorders (quad.blVertice,quad.blColor,-prevOrtho,quad.brVertice,quad.brColor,-ortho,false);
} else {
// What else?
// Parallel lines, no joint necessary
}
} else if (jointType==FTDrawingJointStyle.ROUND) {
if (dot<0) {
float angleDiff=Mathf.Abs(Mathf.Acos(Vector2.Dot(previousCursor.direction,direction)));
int nbQuads=(int)(this.thickness*0.5f*angleDiff *0.5f *0.5f *0.5f)*2+2;
float angle=0;
//2 triangles by quads
float deltaAngle=0.5f*angleDiff/nbQuads;
for (int i=0;i<nbQuads;i++) {
FDrawingQuad quad=new FDrawingQuad(color);
//FDrawingQuad quad=new FDrawingQuad(new Color(1,(float)i/(float)nbQuads,(float)i/(float)nbQuads,1));
quad.trVertice=center;
quad.brVertice=previousCursor.position+prevOrtho*thickness*0.5f*Mathf.Cos(angle)+previousCursor.direction*thickness*0.5f*Mathf.Sin(angle);
angle+=deltaAngle;
quad.blVertice=previousCursor.position+prevOrtho*thickness*0.5f*Mathf.Cos(angle)+previousCursor.direction*thickness*0.5f*Mathf.Sin(angle);
angle+=deltaAngle;
quad.tlVertice=previousCursor.position+prevOrtho*thickness*0.5f*Mathf.Cos(angle)+previousCursor.direction*thickness*0.5f*Mathf.Sin(angle);
_quads.AddLast(quad);
}
} else if (dot>0) {
float angleDiff=Mathf.Abs(Mathf.Acos(Vector2.Dot(previousCursor.direction,direction)));
int nbQuads=(int)(this.thickness*0.5f*angleDiff *0.5f *0.5f *0.5f)*2+2;
float angle=0;
//2 triangles by quads
float deltaAngle=0.5f*angleDiff/nbQuads;
for (int i=0;i<nbQuads;i++) {
FDrawingQuad quad=new FDrawingQuad(color);
//FDrawingQuad quad=new FDrawingQuad(new Color(1,(float)i/(float)nbQuads,(float)i/(float)nbQuads,1));
quad.trVertice=center;
quad.brVertice=previousCursor.position-prevOrtho*thickness*0.5f*Mathf.Cos(-angle)+previousCursor.direction*thickness*0.5f*Mathf.Sin(-angle);
angle-=deltaAngle;
quad.blVertice=previousCursor.position-prevOrtho*thickness*0.5f*Mathf.Cos(-angle)+previousCursor.direction*thickness*0.5f*Mathf.Sin(-angle);
angle-=deltaAngle;
quad.tlVertice=previousCursor.position-prevOrtho*thickness*0.5f*Mathf.Cos(-angle)+previousCursor.direction*thickness*0.5f*Mathf.Sin(-angle);
_quads.AddLast(quad);
}
} else {
// What else?
// Parallel lines, no joint necessary
}
if ((borders!=null)&&(borders.Count>0)) {
throw new FutileException("Joint type "+jointType+" not suported with borders. "+supportedJointTypesWithBorders);
}
} else if (jointType==FTDrawingJointStyle.MITER) {
if (dot<0) {
FDrawingQuad quad=new FDrawingQuad(this.color);
quad.tlVertice=previousCursor.position+prevOrtho*previousCursor.thickness*0.5f;
quad.blVertice=center;
quad.brVertice=lineFromPosition+ortho*thickness*0.5f;
bool valid;
quad.trVertice=VectorUtils.LinesIntersectPoint(quad.tlVertice, quad.tlVertice+previousCursor.direction, quad.brVertice, quad.brVertice-direction, out valid);
if (!valid) {
quad.trVertice=quad.brVertice;
}
_quads.AddLast(quad);
} else if (dot>0) {
FDrawingQuad quad=new FDrawingQuad(this.color);
quad.blVertice=previousCursor.position-prevOrtho*previousCursor.thickness*0.5f;
quad.tlVertice=center;
quad.trVertice=lineFromPosition-ortho*thickness*0.5f;
bool valid;
quad.brVertice=VectorUtils.LinesIntersectPoint(quad.blVertice, quad.blVertice-previousCursor.direction, quad.trVertice, quad.trVertice+direction, out valid);
if (!valid) {
quad.brVertice=quad.trVertice;
}
_quads.AddLast(quad);
} else {
// What else?
// Parallel lines, no joint necessary
}
if ((borders!=null)&&(borders.Count>0)) {
throw new FutileException("Joint type "+jointType+" not suported with borders. "+supportedJointTypesWithBorders);
}
}
}
virtual public void PushBorder(float thickness, Color color, bool gradient, bool top, bool bottom) {
if (borders==null) {
borders=new List<FDrawingBorder>();
}
borders.Add (new FDrawingBorder(thickness, color, gradient, top, bottom));
}
virtual public bool PopBorder() {
if (borders!=null) {
if (borders.Count>0) {
borders.RemoveAt(borders.Count-1);
return true;
}
}
return false;
}
virtual public bool ClearBorders() {
if (borders!=null) {
if (borders.Count>0) {
borders.Clear();
return true;
}
}
return false;
}
}
public class FDrawingQuad
{
public Vector2 tlVertice,blVertice,brVertice,trVertice;
public Color tlColor,blColor,brColor,trColor;
public FDrawingQuad(Color color):base()
{
tlColor=blColor=brColor=trColor=color;
}
public bool SetLineVertices(Vector2 fromPosition,Vector2 toPosition,float thickness, FDrawingCursor cursor)
{
Vector2 direction=toPosition-fromPosition;
float dist=Mathf.Sqrt(Vector2.SqrMagnitude(direction));
if (dist<0.5f) {
return false;
}
direction/=dist;
Vector2 ortho=new Vector2(-direction.y,direction.x);
tlVertice=fromPosition+ortho*thickness*0.5f;
blVertice=fromPosition-ortho*thickness*0.5f;
trVertice=toPosition+ortho*thickness*0.5f;
brVertice=toPosition-ortho*thickness*0.5f;
if (cursor!=null) cursor.direction=direction;
return true;
}
}
public class FDrawingSprite : FSprite
{
protected FDrawingCursor _cursor,_firstLineCursor,_previousLineCursor;
protected LinkedList<FDrawingQuad> _quads;
public FDrawingSprite() : base(Futile.whiteElement)
{
}
public FDrawingSprite (string elementName) : this(Futile.atlasManager.GetElementWithName(elementName))
{
}
public FDrawingSprite (FAtlasElement element) : base()
{
_quads=new LinkedList<FDrawingQuad>();
_cursor=new FDrawingCursor(_quads);
_firstLineCursor=null;
_previousLineCursor=null;
Init(FFacetType.Quad, element,0); //this will call HandleElementChanged(), which will call Setup();
_isAlphaDirty = true;
UpdateLocalVertices();
}
override public void HandleElementChanged()
{
Setup();
}
virtual public void Setup ()
{
_areLocalVerticesDirty = true;
_numberOfFacetsNeeded=16;
if(_isOnStage) _stage.HandleFacetsChanged();
}
private void CheckNeedForMoreFacets() {
while (_quads.Count>_numberOfFacetsNeeded) {
_numberOfFacetsNeeded*=2;
if(_isOnStage) _stage.HandleFacetsChanged();
}
}
override public void UpdateLocalVertices()
{
_areLocalVerticesDirty = false;
}
override public void PopulateRenderLayer()
{
if(_isOnStage && _firstFacetIndex != -1)
{
_isMeshDirty = false;
int vertexIndex0=_firstFacetIndex*4;
foreach (FDrawingQuad quad in _quads) {
int vertexIndex1 = vertexIndex0 + 1;
int vertexIndex2 = vertexIndex0 + 2;
int vertexIndex3 = vertexIndex0 + 3;
Vector3[] vertices = _renderLayer.vertices;
Vector2[] uvs = _renderLayer.uvs;
Color[] colors = _renderLayer.colors;
_concatenatedMatrix.ApplyVector3FromLocalVector2(ref vertices[vertexIndex0], quad.tlVertice,0);
_concatenatedMatrix.ApplyVector3FromLocalVector2(ref vertices[vertexIndex1], quad.blVertice,0);
_concatenatedMatrix.ApplyVector3FromLocalVector2(ref vertices[vertexIndex2], quad.brVertice,0);
_concatenatedMatrix.ApplyVector3FromLocalVector2(ref vertices[vertexIndex3], quad.trVertice,0);
uvs[vertexIndex0] = _element.uvTopLeft;
uvs[vertexIndex1] = _element.uvTopRight;
uvs[vertexIndex2] = _element.uvBottomRight;
uvs[vertexIndex3] = _element.uvBottomLeft;
if (_concatenatedAlpha<1f) {
colors[vertexIndex0] = quad.tlColor.CloneWithMultipliedAlpha(_concatenatedAlpha);
colors[vertexIndex1] = quad.blColor.CloneWithMultipliedAlpha(_concatenatedAlpha);
colors[vertexIndex2] = quad.brColor.CloneWithMultipliedAlpha(_concatenatedAlpha);
colors[vertexIndex3] = quad.trColor.CloneWithMultipliedAlpha(_concatenatedAlpha);
} else {
colors[vertexIndex0] = quad.tlColor;
colors[vertexIndex1] = quad.blColor;
colors[vertexIndex2] = quad.brColor;
colors[vertexIndex3] = quad.trColor;
}
vertexIndex0+=4;
_renderLayer.HandleVertsChange();
}
Vector3 dummyVector3=new Vector3(1000000,100000,100000);
Color dummyColor=new Color(0,0,0,0);
for (int i=_quads.Count;i<_numberOfFacetsNeeded;i++) {
int vertexIndex1 = vertexIndex0 + 1;
int vertexIndex2 = vertexIndex0 + 2;
int vertexIndex3 = vertexIndex0 + 3;
Vector3[] vertices = _renderLayer.vertices;
Vector2[] uvs = _renderLayer.uvs;
Color[] colors = _renderLayer.colors;
vertices[vertexIndex0]=vertices[vertexIndex1]=vertices[vertexIndex2]=vertices[vertexIndex3]=dummyVector3;
uvs[vertexIndex0] = uvs[vertexIndex1] = uvs[vertexIndex2] = uvs[vertexIndex3] = _element.uvBottomLeft;
colors[vertexIndex0] = colors[vertexIndex1] = colors[vertexIndex2] = colors[vertexIndex3] = dummyColor;
vertexIndex0+=4;
_renderLayer.HandleVertsChange();
}
}
}
virtual public void Clear()
{
_quads=new LinkedList<FDrawingQuad>();
_cursor=new FDrawingCursor(_quads);
_firstLineCursor=null;
_previousLineCursor=null;
//Init(FFacetType.Quad, element,0); //this will call HandleElementChanged(), which will call Setup();
_isAlphaDirty = true;
UpdateLocalVertices();
Setup();
}
virtual public void MoveTo(float x,float y)
{
_cursor.MoveTo(x,y,_firstLineCursor);
_firstLineCursor=null;
_previousLineCursor=null;
CheckNeedForMoreFacets();
_isMeshDirty=true;
}
virtual public void LineTo(float x,float y)
{
if (_cursor.LineTo(x,y,_previousLineCursor)) {
CheckNeedForMoreFacets();
if (_previousLineCursor==null) {
_previousLineCursor=_cursor.Clone();
} else {
_previousLineCursor.Copy(_cursor);
}
if (_firstLineCursor==null) {
_firstLineCursor=_cursor.Clone();
}
_isMeshDirty=true;
}
}
virtual public void Loop()
{
_cursor.Loop(_firstLineCursor,_previousLineCursor);
_firstLineCursor=null;
_previousLineCursor=null;
CheckNeedForMoreFacets();
_isMeshDirty=true;
}
virtual public void Flush()
{
_cursor.Flush(_firstLineCursor);
_firstLineCursor=null;
_previousLineCursor=null;
CheckNeedForMoreFacets();
_isMeshDirty=true;
}
virtual public void SetLineColor(Color color,float thickness,FTDrawingJointStyle jointType,FTDrawingCapStyle capType)
{
Flush();
_cursor.color=color;
_cursor.thickness=thickness;
_cursor.jointType=jointType;
_cursor.capType=capType;
}
virtual public void SetLineColor(Color color)
{
Flush();
_cursor.color=color;
}
virtual public void SetLineThickness(float thickness)
{
Flush();
_cursor.thickness=thickness;
}
virtual public void SetLineJointStyle(FTDrawingJointStyle jointType)
{
Flush();
_cursor.jointType=jointType;
}
virtual public void SetLineCapStyle(FTDrawingCapStyle capType)
{
Flush();
_cursor.capType=capType;
}
virtual public void PushBorder(float thickness, Color color, bool gradient)
{
Flush ();
_cursor.PushBorder(thickness, color, gradient,true, true);
}
virtual public void PushTopBorder(float thickness, Color color, bool gradient) {
Flush ();
_cursor.PushBorder(thickness,color,gradient,true,false);
}
virtual public void PushBottomBorder(float thickness, Color color, bool gradient) {
Flush ();
_cursor.PushBorder(thickness,color,gradient,false,true);
}
virtual public void ClearBorders()
{
if (_cursor.ClearBorders()) {
Flush ();
}
}
virtual public void PopBorder()
{
if (_cursor.PopBorder()) {
Flush ();
}
}
public Vector2 GetCursorPosition() {
return _cursor.position;
}
}
public static class VectorUtils
{
public static bool SegmentsIntersect(Vector2 A, Vector2 B, Vector2 C, Vector2 D)
{
float S, T;
if( VectorUtils.LinesIntersect(A, B, C, D, out S, out T )
&& (S >= 0.0f && S <= 1.0f && T >= 0.0f && T <= 1.0f) )
return true;
return false;
}
public static Vector2 LinesIntersectPoint(Vector2 A, Vector2 B, Vector2 C, Vector2 D, out bool valid)
{
float S, T;
if( VectorUtils.LinesIntersect(A, B, C, D, out S, out T) ) {
// Point of intersection
Vector2 P;
P.x = A.x + S * (B.x - A.x);
P.y = A.y + S * (B.y - A.y);
valid=true;
return P;
}
valid=false;
return Vector2.zero;
}
public static bool LinesIntersect(Vector2 A, Vector2 B,
Vector2 C, Vector2 D,
out float S, out float T)
{
// FAIL: Line undefined
if ( (A.x==B.x && A.y==B.y) || (C.x==D.x && C.y==D.y) ) {
S=T=0;
return false;
}
float BAx = B.x - A.x;
float BAy = B.y - A.y;
float DCx = D.x - C.x;
float DCy = D.y - C.y;
float ACx = A.x - C.x;
float ACy = A.y - C.y;
float denom = DCy*BAx - DCx*BAy;
S = DCx*ACy - DCy*ACx;
T = BAx*ACy - BAy*ACx;
if (denom == 0) {
if (S == 0 || T == 0) {
// Lines incident
return true;
}
// Lines parallel and not incident
return false;
}
S = S / denom;
T = T / denom;
// Point of intersection
// Vector2 P;
// P.x = A.x + *S * (B.x - A.x);
// P.y = A.y + *S * (B.y - A.y);
return true;
}
public static float Angle (Vector2 vector) {
Vector2 to = new Vector2(1, 0);
float result = Vector2.Angle( vector, to );
Vector3 cross = Vector3.Cross( vector, to );
if (cross.z > 0)
result = 360f - result;
return result;
}
}
@EktaMehta
Copy link

hey how to use this class in unity c# script ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment