001package armyc2.c5isr.renderer.utilities; 002 003import android.graphics.Point; 004import android.graphics.PointF; 005import android.graphics.Rect; 006import android.graphics.RectF; 007 008import java.util.ArrayList; 009 010public class SVGPath { 011 012 public static float ACTION_MOVE_TO = 0; 013 public static float ACTION_LINE_TO = 1; 014 public static float ACTION_CURVE_TO = 2;//cubic bezier curve 015 public static float ACTION_QUAD_TO = 3;//quadratic bezier curve 016 public static float ACTION_ARC_TO = 4; 017 public static float ACTION_ARC = 5; 018 public static float ACTION_DASHED_LINE_TO = 6; 019 020 private ArrayList<float[]> _actions = new ArrayList<>(); 021 private String _dashArray = null; 022 private PointF _startPoint=null; 023 private PointF _endPoint=null; 024 private PointF _lastMoveTo = null; 025 private RectF _rectangle = null; 026 private String _method = null;//stroke,fill,fillPattern 027 028 public void setLineDash(String dashArray) 029 { 030 this._dashArray = dashArray; 031 } 032 033 public Rect getBounds() 034 { 035 if(this._rectangle != null) 036 { 037 return RectUtilities.makeRectFromRectF(this._rectangle); 038 } 039 else 040 { 041 return null; 042 } 043 } 044 045 public void shift(int x, int y) 046 { 047 int size = this._actions.size(); 048 float[] temp = null; 049 RectUtilities.shift(this._rectangle,x,y); 050 051 for(int i=0; i<size;i++) 052 { 053 temp = this._actions.get(i); 054 if(temp[0]==ACTION_MOVE_TO) 055 { 056 temp[1] = temp[1] + x; 057 temp[2] = temp[2] + y; 058 } 059 else if(temp[0]==ACTION_LINE_TO) 060 { 061 temp[1] = temp[1] + x; 062 temp[2] = temp[2] + y; 063 } 064 else if(temp[0]==ACTION_CURVE_TO) 065 { 066 temp[1] = temp[1] + x; 067 temp[2] = temp[2] + y; 068 temp[3] = temp[3] + x; 069 temp[4] = temp[4] + y; 070 temp[5] = temp[5] + x; 071 temp[6] = temp[6] + y; 072 } 073 else if(temp[0]==ACTION_QUAD_TO) 074 { 075 temp[1] = temp[1] + x; 076 temp[2] = temp[2] + y; 077 temp[3] = temp[3] + x; 078 temp[4] = temp[4] + y; 079 } 080 else if(temp[0]==ACTION_ARC_TO) 081 { 082 temp[1] = temp[1] + x; 083 temp[2] = temp[2] + y; 084 temp[3] = temp[3] + x; 085 temp[4] = temp[4] + y; 086 } 087 else if(temp[0]==ACTION_ARC) 088 { 089 temp[1] = temp[1] + x; 090 temp[2] = temp[2] + y; 091 } 092 } 093 094 this._startPoint.offset(x,y); 095 this._endPoint.offset(x,y); 096 this._lastMoveTo.offset(x,y); 097 } 098 099 /** 100 * The number of this._actions on the path 101 */ 102 public int getLength() 103 { 104 return this._actions.size(); 105 }; 106 107 /** 108 * Adds a point to the path by moving to the specified coordinates specified 109 * @param x 110 * @param y 111 */ 112 public void moveTo(float x, float y) 113 { 114 115 if(this._actions.size() == 0) 116 { 117 this._rectangle = new RectF(x,y,1,1); 118 this._startPoint = new PointF(x,y); 119 this._endPoint = new PointF(x,y); 120 //curr_startPoint = new armyc2.c2sd.renderer.Point(x,y); 121 //curr_endPoint = new armyc2.c2sd.renderer.Point(x,y); 122 } 123 this._rectangle.union(x,y); 124 float[] actions = {ACTION_MOVE_TO,x,y}; 125 this._actions.add(actions); 126 this._lastMoveTo = new PointF(x,y); 127 this._endPoint = new PointF(x,y); 128 } 129 130 /** 131 * Adds a point to the path by drawing a straight line from the current 132 * coordinates to the new specified coordinates specified 133 * @param x 134 * @param y 135 */ 136 public void lineTo(float x, float y) 137 { 138 139 if(this._actions.size() == 0) 140 { 141 this.moveTo(0,0); 142 } 143 float[] actions = {ACTION_LINE_TO,x,y}; 144 this._actions.add(actions); 145 this._rectangle.union(x,y); 146 this._endPoint = new PointF(x,y); 147 } 148 149 /** 150 * Adds a curved segment, defined by three new points, to the path by 151 * drawing a Bézier curve that intersects both the current coordinates 152 * and the specified coordinates (x,y), using the specified points 153 * (cp1x,xp1y) and (cp2x,cp2y) as Bézier control points. 154 * @param cp1x 155 * @param cp1y 156 * @param cp2x 157 * @param cp2y 158 * @param x 159 * @param y 160 */ 161 public void bezierCurveTo(float cp1x, float cp1y, float cp2x, float cp2y, float x, float y){ 162 163 if(this._actions.size() == 0) 164 { 165 this.moveTo(0,0); 166 } 167 float[] actions = {ACTION_CURVE_TO,cp1x,cp1y,cp2x,cp2y,x,y}; 168 this._actions.add(actions); 169 this._rectangle.union(cp1x,cp1y); 170 this._rectangle.union(cp2x,cp2y); 171 this._rectangle.union(x,y); 172 this._endPoint = new PointF(x,y); 173 } 174 175 /** 176 * Adds a curved segment, defined by two new points, to the path by 177 * drawing a Quadratic curve that intersects both the current 178 * coordinates and the specified coordinates (x,y), using the 179 * specified point (cpx,cpy) as a quadratic parametric control point. 180 * @param cpx 181 * @param cpy 182 * @param x 183 * @param y 184 * @returns 185 */ 186 public void quadraticCurveTo(float cpx, float cpy, float x, float y) 187 { 188 if(this._actions.size() == 0) 189 { 190 this.moveTo(0,0); 191 } 192 float[] actions = {ACTION_QUAD_TO,cpx,cpy,x,y}; 193 this._actions.add(actions); 194 this._rectangle.union(cpx,cpy); 195 this._rectangle.union(x,y); 196 this._endPoint = new PointF(x,y); 197 } 198 199 /** 200 * The arcTo() method creates an arc/curve between two tangents on the canvas. 201 * @param x1 The x-coordinate of the beginning of the arc 202 * @param y1 The y-coordinate of the beginning of the arc 203 * @param x2 The x-coordinate of the end of the arc 204 * @param y2 The y-coordinate of the end of the arc 205 * @param r The radius of the arc 206 * @returns 207 */ 208 public void arcTo(float x1, float y1, float x2, float y2, float r) 209 { 210 if(this._actions.size() == 0) 211 { 212 this.moveTo(0,0); 213 } 214 float[] actions = {ACTION_ARC_TO,x1,y1,x2,y2}; 215 this._actions.add(actions); 216 this._rectangle.union(x1,y1); 217 this._rectangle.union(x2,y2); 218 this._endPoint = new PointF(x2,y2); 219 } 220 221 /** 222 * The arc() method creates an arc/curve 223 * (use to create circles. or parts of circles). 224 * @param x The x-coordinate of the center of the circle 225 * @param y The y-coordinate of the center of the circle 226 * @param r The radius of the circle 227 * @param sAngle The starting angle, in degrees 228 * (0 is at the 3 -'clock position of the arc's circle) 229 * @param eAngle The ending angle, in degrees 230 * @param counterclockwise Optional. Specifies wheter the drawing 231 * should be counterclockwise or clockwise. False=clockwise, 232 * true=counter-clockwise; 233 * @returns 234 */ 235 public void arc(float x, float y, float r, float sAngle, float eAngle, boolean counterclockwise) 236 { 237/* 238 if(counterclockwise != true) 239 { 240 counterclockwise = false; 241 } 242 243 //degrees to radians 244 float sa = (float)(sAngle * (Math.PI / 180)); 245 float ea = (float)(eAngle * (Math.PI / 180)); 246 247 248 if(this._startPoint===null) 249 { 250 double sX = r * Math.cos(sa) + x; 251 double sY = r * Math.sin(sa) + y; 252 this._startPoint = new PointF((float)sX,(float)sY); 253 this._rectangle = new RectF((float)sX,(float)sY,1,1); 254 } 255 256 float[] actions = {ACTION_ARC,x,y,r,sa,ea,counterclockwise}; 257 this._actions.add(actions); 258 this._rectangle.union(new Rectangle(x-r,y-r,r*2,r*2)); 259 260 var newX = r * Math.cos(ea) + x; 261 var newY = r * Math.sin(ea) + y; 262 this._endPoint = new Point(newX,newY); 263 this.moveTo(newX,newY);//*/ 264 265 } 266 267 /** 268 * Arc and ArcTo do not covert currently 269 */ 270 public String toSVGElement(String stroke, float strokeWidth, String fill, float strokeOpacity, float fillOpacity, String lineCap) 271 { 272 int format = 1; 273 274 275 //context.beginPath(); 276 int size = this._actions.size(); 277 float[] temp = null; 278 String path = ""; 279 String line = null; 280 281 try { 282 for (int i = 0; i < size; i++) 283 { 284 temp = this._actions.get(i); 285 286 /*if(path !== "") 287 path += " ";*/ 288 289 if (temp[0] == ACTION_LINE_TO) { 290 path += "L" + temp[1] + " " + temp[2]; 291 //context.lineTo(temp[1],temp[2]); 292 } else if (temp[0] == ACTION_MOVE_TO) { 293 //context.moveTo(temp[1],temp[2]); 294 295 if (i == 0 || this._method != "fillPattern") { 296 path += "M" + temp[1] + " " + temp[2]; 297 //context.moveTo(temp[1],temp[2]); 298 } else//no moves in a fill shape except maybe for the first one 299 { 300 path += "L" + temp[1] + " " + temp[2]; 301 //context.lineTo(temp[1],temp[2]); 302 }//*/ 303 } else if (temp[0] == ACTION_DASHED_LINE_TO) { 304 path += "L" + temp[3] + " " + temp[4]; 305 /*if(this._method === "stroke") 306 { 307 context.dashedLineTo(temp[1],temp[2],temp[3],temp[4],temp[5]); 308 } 309 else //you don't dash a fill shape 310 { 311 context.lineTo(temp[3],temp[4]); 312 }//*/ 313 } else if (temp[0] == ACTION_CURVE_TO) { 314 //C100 100 250 100 250 200 315 path += "C" + temp[1] + " " + temp[2] + " " + temp[3] + " " + temp[4] + " " + temp[5] + " " + temp[6]; 316 //context.bezierCurveTo(temp[1],temp[2],temp[3],temp[4],temp[5],temp[6]); 317 } else if (temp[0] == ACTION_QUAD_TO) { 318 path += "Q" + temp[1] + " " + temp[2] + " " + temp[3] + " " + temp[4]; 319 //context.quadraticCurveTo(temp[1],temp[2],temp[3],temp[4]); 320 } else if (temp[0] == ACTION_ARC_TO) { 321 //path += "C" + temp[1] + " " + temp[2] + " " + temp[3] + " " + temp[4] + " " + temp[5]; 322 //context.arcTo(temp[1],temp[2],temp[3],temp[4],temp[5]); 323 } else if (temp[0] == ACTION_ARC) { 324 //context.arc(temp[1],temp[2],temp[3],temp[4],temp[5],temp[6]); 325 }//*/ 326 } 327 //TODO: generate path svg element 328 line = "<path d=\"" + path + "\""; 329 330 if (stroke != null) { 331 332 line += " stroke=\"" + stroke + "\""; 333 /*else 334 line += ' stroke="' + stroke.replace(/#/g,"#") + '"';*/ 335 336 if (strokeWidth > 0) 337 line += " stroke-width=\"" + strokeWidth + '"'; 338 else 339 line += " stroke-width=\"2\""; 340 341 if (strokeOpacity != 1.0) { 342 //stroke-opacity="0.4" 343 line += " stroke-opacity=\"" + strokeOpacity + "\""; 344 } 345 346 if(lineCap != null && 347 (lineCap.equalsIgnoreCase("butt") || 348 lineCap.equalsIgnoreCase("round") || 349 lineCap.equalsIgnoreCase("square"))) 350 { 351 line += " stroke-linecap=\"" + lineCap + "\""; 352 } 353 else 354 line += " stroke-linecap=\"round\""; 355 } 356 357 if (this._dashArray != null) 358 line += " stroke-dasharray=\"" + this._dashArray + "\""; 359 360 if (fill != null) { 361 if (fill.indexOf("url") == 0) { 362 line += " fill=\"url(#fillPattern)\""; 363 //line += ' fill="url(#fillPattern)"'; 364 } else { 365 //line += ' fill="' + fill + '"'; 366 line += " fill=\"" + fill + '"';//text = text.replace(/\</g,">"); 367 /*else 368 line += ' fill="' + fill.replace(/#/g,"#") + '"';//text = text.replace(/\</g,">");*/ 369 370 if (fillOpacity != 1.0) { 371 //fill-opacity="0.4" 372 line += " fill-opacity=\"" + fillOpacity + "\""; 373 } 374 } 375 376 } else 377 line += " fill=\"none\""; 378 379 line += " />"; 380 } 381 catch(Exception exc) 382 { 383 ErrorLogger.LogException("SVGPath", "toSVGElement", exc); 384 line = null; 385 } 386 return line; 387 388 } 389 390}