ezySVG Lib for Freebasic - make SVG files

Headers, Bindings, Libraries for use with FreeBASIC, Please include example of use to help ensure they are tested and usable.
Post Reply
Tonigau
Posts: 36
Joined: Feb 25, 2021 20:19

ezySVG Lib for Freebasic - make SVG files

Post by Tonigau »

I made a lib ezySVG to produce SVG code from a Freebasic program.
This started out as a need to get vector data points into CDRx4 CAD from my logic analyzer, it worked easily by making an SVG element 'polyline' with the samples of logic state. The polyline becomes 1 graphic object in CAD (easy to manipulate)
Polyline SVG for import:

Code: Select all

<svg height="200" width="500">
   <polyline points=" 0,0 22.9031,0 22.9031,10 24.99491,10 24.99491,0 27.85016,0 27.85016, .... "
     style="fill:none;stroke:blue;stroke-width:0.1"
   />
</svg>
Then I had a CSV DataGraph app in Freebasic using lib window9.bi, I structured the SVG function parameters to 'shadow' the window9 graphic commands & this is where the ezySVG.bi lib was developed.
See VectorGraph_Example.bas

It should be noted that SVG rendering apps ( web browsers, vector graphic CAD/viewers, online converters etc.) may not display correctly or at all some SVG elements or even not load an SVG file. I have tested only with firefox browser & some online validators/converters. Different SVG versions does not help especially with obsolete syntax like 'xlink:href' ...
We need to isolate the part that is not working & edit the SVG to test. It might be just a Ucase chr or all Lcase is needed in a name or value. skewX, preserveAspectRatio="xMidyMid" , stroke-dasharray, are a few.

SVG: (for the scope of this lib)
SVG file has a Start (size, offset) - - - Contents - - - End
The contents are typically Elements with attributes & style, groups, definitions etc...
Refer the following for detail.
https://www.w3schools.com/graphics/svg_intro.asp
https://developer.mozilla.org/en-US/doc ... troduction
I would not bother trying to digest too much of the SVG specification, it's quite intense(the links above are more helpful).

Lib specifics:
ezySVG lib takes care of the SVG line structure but you must be syntactically correct with style names & value.
please refer the example bas files & SVG reference documents. The lib does not check name/value SVG syntax.
The style is just 1 string passed to the lib, it can be literal or you programmatically set values & concatenate.
You may use leading Ucase name like Fill=green if you prefer, but it may cause some issue. You can turn on force Lcase by adding #Define L_Case at the start of bas code, this will lower any Ucase characters of style name. (just view the SVG code)
Transform attributes are handled by the lib, these can be any case. (Rotate, Scale, SkewX, SkewY, Translate, Matrix)
The commands SVG_Start & SVG_End have an optional parameter value "html", this makes the SVG 'html ready' for easy open with a web browser. This is ideal for testing or print to Vector pdf. In acrobat I can also export to raster image.
With the SVG image displayed, right click 'View Page Source' to see syntax highlighted SVG code.
To debug SVG open it in your browser, observe what is not working, 'view page source' then open the SVG in your favorite text editor (preferably with xml/html syntax colors). hack the code to get it working. If you think it's a lib issue, please report.
If you have a SVG code snippet that should be working, try pasting into a html 'tryit' editor (& set values accordingly)

ezySVG lib has limitations in what can be done with SVG in comparison to SVG's vast capability.It is fairly easy to add new features & if significant could be ezySVG_2.
Most functions(sub's actually) were made by copy/paste existing sub & edit specifics.

ezySVG.bi V0.92 2024-02-15

Code: Select all

' ezySVG.bi
' Make SVG from Freebasic code            ToniG      Create: 2023.12.20
'                                         File Version V0.92.2 2024-02-17 

'This lib creates SVG elements, it is minimal & can be expanded to include additional functionality.

'Supported Elements:
'                      Line, Polyline, Polygon, Text, Rectangle, Circle, Elipse, Path, Image
'Supported Attributes:
'                      Many (to name a few... Fill, Stroke, Font-Family, Font-Size, Font-Weight, Font-Style)

'                      Transform - Rotate, Scale, Translate, SkewX, SkewY, Matrix(not tested)
'                      Transform-Origin (minimal tested)

'Special function:
'                      Quadratic Curve, Quadratic Curve Multi, SVG Group, Text on Path, Gradient fill
'                      DropShadow, GaussianBlur, SVG Comment, SVG_CRLF, SVG_RAW

'Notes:
'     Some SVG functionality(transform, +other) may only work in a web browser (or capable viewer). 
'     Import to CAD may need to keep simple as possible or explicity create elements. Test & see what works.
'     Use an online SVG converter to make vector cad (AI, EPS, PDF) 
'     For now the Element co-ordinates & size are Integer, for screen rendering 
'     if want to use  dp precision then as Single needed. 

'USAGE:    Refer example bas files & SVG reference info.

'-----------------------------------
' Can use Ucase or Lcase for style strings as the lib will convert to Lcase. (except some specific Ucase requirements) 

' Setting the style string with fixed values can be done with just 1 string eg. " stroke=rgb(0,0,0), stroke-width=0.5"
' to add variables for style attribute value(parameter) use eg. "stroke-width=" +Str(VarValue)
'
' To rotate a path, create the path points in a virtual bounding box (Min Max X&Y values) then use the mid point XY
' in the Transform=rotate(R,X,Y) Its not easy to resolve XY mid from complex path values. (SVG renderers do it though)

'-----------------------------------
' Changes:  Since V0.10
'   V0.12 2024-02-07    Change Group parameter to Style set = Group Start, Style Null = Group End
'   V0.13 2024-02-10    Fixed Bug in transform Case Else (Element attr killed if Transform active) 
'   V0.90 2024-02-10    Add Text on Path, Gradient fill. 
'   V0.91 2024-02-15    Add Drop Shadow filter, change style separator from"_" to "__". 
'                       Add code to escape "&" --> "&#38;" in text strings only.
'                       Fix Var type PolyBB & rename PolyBBox
'                       Fix 'File path check' in Sub SVG_Image (filename only/Full path/online resource)
'   V0.92 2024-02-15    Add GaussianBlur, change blur param to Single (also for drop shadow)
'   V0.92.1 2024-02-16  Change order of SVG_Qbezier parameters to [x1,x2, Qx,Qy, x2,y2,...] to be consistent with "path points"
'   V0.92.2 2024-02-17  Change SVG_Qbezier to use SVG_Path sub, Add CRLF indenting for SVGpath string.
'                       Add optional parameter 7 "xmlns" to SVG_Start, some minor fixes 
'         
'ToDoo:
'      1. ...

'Issues:
'      1. There May be an issue with Style string left part processing for non Transform attributes

'-----------------------------------

Declare Sub SVG_Start(SVGoutFile As String, SVG_SizeX As Long, SVG_SizeY As Long, Offset_X As integer=0, Offset_Y As integer=0, HTML_ON As String="", SVGstr As String="")
Declare Sub SVG_End(HTML_ON As String="")
Declare Sub SVG_PolyLine(SVGpoints As String, ByVal StyleSVG As String="", RotateVal As Integer=0, StyleFmt As UByte=1)
Declare Sub SVG_Line(X1 As Long, Y1 As Long, X2 As Long, Y2 As Long, ByVal StyleSVG As String="", RotateVal As Integer=0, StyleFmt As UByte=1) 

Declare Sub SVG_Group(ByVal StyleSVG As String="")
Declare Sub SVG_Text(X As Long, Y As Long, TXTstr As String, ByVal StyleSVG As String="", RotateVal As Integer=0, StyleFmt As UByte=1)
Declare Sub SVG_Rect(X As Long, Y As Long, W As Long, H As Long, ByVal StyleSVG As String="", RotateVal As Integer=0, StyleFmt As UByte=1)
Declare Sub SVG_Circle(Cx As Long, Cy As Long, R As Long, ByVal StyleSVG As String="", RotateVal As Integer=0, StyleFmt As UByte=1)  

Declare Sub SVG_Ellipse(Cx As Long, Cy As Long, Rx As Long, Ry As long, ByVal StyleSVG As String="", RotateVal As Integer=0, StyleFmt As UByte=1)
Declare Sub SVG_Polygon(SVGpoints As String, ByVal StyleSVG As String="", RotateVal As Integer=0, StyleFmt As UByte=1)
Declare Sub SVG_Qbezier(X1 As Long, Y1 As Long, X2 As Long, Y2 As Long, QX As Long, QY As Long, ByVal StyleSVG As String="", RotateVal As Integer=0, StyleFmt As UByte=1)
' Obsolete Declare Sub SVG_QbezierM(ByVal QbezStr As String, ByVal StyleSVG As String="", StyleFmt As UByte=1)
        
Declare Sub SVG_Path(ByVal SVGpathStr As String, ByVal StyleSVG As String="", RotateVal As Integer=0, StyleFmt As UByte=1)
Declare Sub SVG_TextPath(Path_ID As String, PathPoints As String, Path_Style As String, TXTstr As String, Text_Style As String,  Strt_Pos As UByte=10, ShowPath As Integer=1)
Declare Sub SVG_GradientL(Grad_ID As String, Color_1 As String, Color_2 As String, Stop1 As Integer, Stop2 As Integer, X1pc As Integer, X2pc As Integer, Y1pc As Integer, Y2pc As Integer)
Declare Sub SVG_DropShadow(DS_ID As String, Color_1 As String, DX As Integer=10, DY As Integer=10, Blur As Single=2, feOffset As Integer= -20, feSize As Integer=180)    
Declare Sub SVG_GaussianBlur(DS_ID As String, DX As Integer=10, DY As Integer=10, Blur As Single=2, feOffset As Integer= -20, feSize As Integer=180)
Declare Sub SVG_Image(X As Long, Y As Long, W As Long, H As Long, ByVal KeepAspect As String="", ByVal ImgPathFile As String, RotateVal As Integer=0, StyleFmt As UByte=1)
Declare Sub SVG_Comment(TXTstr As String, LFCR As UByte=0)
Declare Sub SVG_CRLF(n_ As Ubyte =1)
Declare Sub SVG_RAW(TextIn As String)

Declare Function SVG_StyleRet1(ByVal AttVal_1 As String="", ByVal AttVal_2 As String="", ByVal AttVal_3 As String="", ByVal AttVal_4 As String="", ByVal AttVal_5 As String="") As String    
Declare Function SVG_StyleRet2(ByVal AttName_1 As String="", ByVal AttValu_1 As Single=0, ByVal AttName_2 As String="", ByVal AttValu_2 As Single=0, _
                               ByVal AttName_3 As String="", ByVal AttValu_3 As Single=0, ByVal AttName_4 As String="", ByVal AttValu_4 As Single=0) As String
Declare Function SVG_GetPolyMid(byVal PolyPts As String, PolyBBox() As Long) As Integer

 'Internal functions (not for main program use)
Declare Function FormatStyle(ByVal StyleSVG As String, ByVal Xs As Integer, ByVal Ys As Integer, ByVal StyleFmt As Ubyte) As String 
Declare Function FormatStyle2(ByVal StyleSVG As String, ByVal Xs As Integer, ByVal Ys As Integer, ByVal StyleFmt As UByte, Byval Part As Ubyte) As String
Declare Function FindAndChr(String1 As String) As String

Dim Shared As Ubyte SVG_Fnum     ' SVG File Number

#Define CR_LF Chr(13,10)
#Define DQ    Chr(34)


Sub SVG_Start(SVGoutFile As String, SVG_SizeX As Long, SVG_SizeY As Long, Offset_X As Integer = 0, Offset_Y As Integer = 0, HTML_ON As String = "", SVGstr As String="")
    Dim As String   HTML_head
    HTML_head = "<!DOCTYPE html>" +CR_LF + _ ' easier to open for browser
                "<html>" +CR_LF + _
                "<body>" +CR_LF
    SVG_Fnum = FreeFile
      If UCase(Trim(HTML_ON)) = "HTML" Then SVGoutFile += ".html" 
    Open SVGoutFile for Output As #SVG_Fnum
      If UCase(Trim(HTML_ON)) = "HTML" Then Print #SVG_Fnum, HTML_head
      'SVG 
      Print #SVG_Fnum,         "<svg";
      Print #SVG_Fnum,  " width="; DQ; Str(SVG_SizeX); DQ ;
      Print #SVG_Fnum,  " height="; DQ; Str(SVG_SizeY); DQ ;
      Print #SVG_Fnum,  " viewbox=" +DQ +Str(Offset_X) +"," +Str(Offset_Y) +"," +Str(SVG_SizeX) +"," +Str(SVG_SizeY)+DQ +" " +SVGstr;
      Print #SVG_Fnum,            ">"
End Sub


Sub SVG_End(HTML_ON As String = "")
      Dim As String  HTML_End
      HTML_End = CR_LF +"</body>" +CR_LF _
                       +"</html>"
      Print #SVG_Fnum, "</svg>"
      If UCase(Trim(HTML_ON)) = "HTML" Then  Print #SVG_Fnum, HTML_End
   Close SVG_Fnum
End Sub


'   With Style set is Group start, with Style NULL(or less than 6 chrs) = Group End
Sub SVG_Group(ByVal Style As String = "")
   Dim As String  TmpStr 'RetSep(1 To 2)
   If  Len(Trim(Style)) < 6 Then Style = ""
   If Style <> "" Then
      TmpStr = "<g" +CR_LF +FormatStyle(Style, 0, 0, 0)
      Print #SVG_Fnum, TmpStr +CR_LF +">"
   Else
      Print #SVG_Fnum, "</g>" +CR_LF   ' group end
   EndIf
End Sub  

'  Alternate Group End
Sub SVG_GroupEnd()
      Print #SVG_Fnum, "</g>" +CR_LF   ' group end
End Sub  


Sub SVG_Rect(X As Long, Y As Long, W As Long, H As Long, ByVal StyleSVG As String="", RotateVal As Integer=0, StyleFmt As UByte=1)
  Dim As UByte Pos1
  Dim As String Indent1, ElemAttr1, ElementStr1, ElemAttr2, TmpStr
    Indent1 = "" : If StyleFmt = 0 Then Indent1 = "   " '
    ElementStr1 = Indent1 +"<rect"
    ElemAttr1 = " x=" +DQ +Str(X) +DQ +" y=" +DQ +Str(Y) +DQ +" width=" +DQ +Str(W) +DQ +" height=" +DQ +Str(H) +DQ
   If RotateVal <> 0 Then ElemAttr2 = " transform=""rotate(" +Str(RotateVal) +"," +Str(X+W/2) +"," +Str(Y+H/2)  +")"""
    ElementStr1 &= ElemAttr1 + ElemAttr2                                  ' Rotate replaces x y with a translate(x,y)
   If StyleSVG <> "" Then TmpStr = FormatStyle(StyleSVG, X, Y, StyleFmt) 
    Print #SVG_Fnum, ElementStr1; TmpStr; "/>" '+ CR_LF
End Sub


Sub SVG_PolyLine(SVGpoints As String, ByVal StyleSVG As String="", RotateVal As Integer=0, StyleFmt As UByte=1)
  Dim As UByte Pos1
  Dim As Long PolyBBox(1 To 10) ' polygon bounding box(MinMax & CTR)
  Dim As String Indent1, ElemAttr1, ElementStr1, ElemAttr2, TmpStr
    Indent1 = "" : If StyleFmt = 0 Then Indent1 = "   " '
    ElementStr1 = Indent1 +"<polyline points= "   '< change this for other element type & edit ElemAttr1, ElemAttr2 
    ElemAttr1   = DQ+ SVGpoints +DQ     
   SVG_GetPolyMid(SVGpoints, PolyBBox()) ' find centre
   If RotateVal <> 0 Then 
      ElemAttr2 = " transform=""rotate(" +Str(RotateVal) +"," +Str(PolyBBox(1)) +"," +Str(PolyBBox(2)) +")"""
   End If 
    Print #SVG_Fnum, ElementStr1 +ElemAttr1' +CR_LF 
   TmpStr = FormatStyle(StyleSVG, PolyBBox(1), PolyBBox(2), StyleFmt)
    Print #SVG_Fnum, ElemAttr2 +TmpStr; "/>" '+ CR_LF
End Sub


Sub SVG_Polygon(SVGpoints As String, ByVal StyleSVG As String="", RotateVal As Integer=0, StyleFmt As UByte=1)
  Dim As UByte Pos1
  Dim As Long PolyBBox(1 To 10) ' polygon bounding box(MinMax & CTR)
  Dim As String Indent1, ElemAttr1, ElementStr1, ElemAttr2, TmpStr
    Indent1 = "" : If StyleFmt = 0 Then Indent1 = "   " '
    ElementStr1 = Indent1 +"<polygon points= "
    ElemAttr1   = DQ+ SVGpoints +DQ
   SVG_GetPolyMid(SVGpoints, PolyBBox()) ' find centre
   If RotateVal <> 0 Then 
      ElemAttr2 = " transform=""rotate(" +Str(RotateVal) +"," +Str(PolyBBox(1)) +"," +Str(PolyBBox(2)) +")"""
   End If 
    Print #SVG_Fnum, ElementStr1 +ElemAttr1' +CR_LF 
   TmpStr = FormatStyle(StyleSVG, PolyBBox(1), PolyBBox(2), StyleFmt) 
    Print #SVG_Fnum, ElemAttr2 +TmpStr; "/>" '+ CR_LF
End Sub


Sub SVG_Line(X1 As Long, Y1 As Long, X2 As Long, Y2 As Long, ByVal StyleSVG As String="", RotateVal As Integer=0, StyleFmt As UByte=1)
  Dim As UByte Pos1
  Dim As String Indent1, ElemAttr1, ElementStr1, ElemAttr2, TmpStr
    Indent1 = "" : If StyleFmt = 0 Then Indent1 = "   " : StyleFmt = 2
    ElementStr1 = Indent1 +"<line"
    ElemAttr1   = " x1=" +DQ +Str(X1) +DQ +" y1=" +DQ +Str(Y1) +DQ + " x2=" +DQ +Str(X2) +DQ +" y2=" +DQ +Str(Y2) +DQ
     If RotateVal <> 0 Then ElemAttr2 = " transform=""rotate(" +Str(RotateVal) +"," +Str(X1) +"," +Str(Y1) +")"""
    ElementStr1 &= ElemAttr1 + ElemAttr2            '            
   If StyleSVG <> "" Then TmpStr = FormatStyle(StyleSVG, X1, Y1, StyleFmt) ' X1 Y1 = element pos for rotate
    Print #SVG_Fnum, ElementStr1; TmpStr; "/>" '+ CR_LF
End Sub


Sub SVG_Circle(Cx As Long, Cy As Long, R As Long, ByVal StyleSVG As String="", RotateVal As Integer=0, StyleFmt As UByte=1)
  Dim As UByte Pos1
  Dim As String Indent1, ElemAttr1, ElementStr1, ElemAttr2, TmpStr
    Indent1 = "" : If StyleFmt = 0 Then Indent1 = "   " '
    ElementStr1 = Indent1 +"<circle"
    ElemAttr1   = " cx=" +DQ +Str(Cx) +DQ +" cy=" +DQ +Str(Cy) +DQ + " r=" +DQ +Str(R) +DQ
    ElementStr1 &= ElemAttr1 + ElemAttr2                                  ' Rotate enabled for consistency
    TmpStr = FormatStyle(StyleSVG, Cx, Cy, StyleFmt) 
    Print #SVG_Fnum, ElementStr1; TmpStr; "/>" '+ CR_LF
End Sub


Sub SVG_Ellipse(Cx As Long, Cy As Long, Rx As Long, Ry As long, ByVal StyleSVG As String="", RotateVal As Integer=0, StyleFmt As UByte=1)
  Dim As UByte Pos1
  Dim As String Indent1, ElemAttr1, ElementStr1, ElemAttr2, TmpStr
    Indent1 = "" : If StyleFmt = 0 Then Indent1 = "   " '
    ElementStr1 = Indent1 +"<ellipse"
    ElemAttr1 = " cx=" +DQ +Str(Cx) +DQ +" cy=" +DQ +Str(Cy) +DQ +" rx=" +DQ +Str(Rx) +DQ +" ry=" +DQ +Str(Ry) +DQ 
   If RotateVal <> 0 Then ElemAttr2 = " transform=""rotate(" +Str(RotateVal) +"," +Str(Cx) +"," +Str(cy)  +")"""
    ElementStr1 &= ElemAttr1 + ElemAttr2                                  ' Rotate replaces x y with a translate(x,y)
   If StyleSVG <> "" Then TmpStr = FormatStyle(StyleSVG, Cx, Cy, StyleFmt) 
    Print #SVG_Fnum, ElementStr1; TmpStr; "/>" '+ CR_LF
End Sub


Sub SVG_Text(X As Long, Y As Long, TXTstr As String, ByVal StyleSVG As String="", RotateVal As Integer=0, StyleFmt As UByte=1)
 Dim As UByte Pos1
 Dim As String Indent1, ElemAttr1, ElementStr1, ElemAttr2, TmpStr
    TXTstr = FindAndChr(TXTstr)
    Indent1 = "" : If StyleFmt = 0 Then Indent1 = "   " '
    ElementStr1 = Indent1 +"<text"
    ElemAttr1   = " x=" +DQ +Str(X) +DQ +" y=" +DQ +Str(Y) +DQ 
   If RotateVal <> 0 Then ElemAttr2 = " transform=""rotate(" +Str(RotateVal) +"," +Str(X) +"," +Str(Y) +")"""
    ElementStr1 &= ElemAttr1 + ElemAttr2
      TmpStr =   FormatStyle(StyleSVG, X, Y, StyleFmt)   'X Y need not for text ?? 
    Print #SVG_Fnum, ElementStr1; " "; TmpStr; ">"; TXTstr; "</text>" '+ CR_LF
End Sub


Sub SVG_TextPath(Path_ID As String, PathPoints As String, Path_Style As String, TXTstr As String, Text_Style As String, Strt_Pos As UByte=10, ShowPath As Integer=1)
   Dim As String Def_Str, Text_Str, Indent1 = "  "
    TXTstr = FindAndChr(TXTstr) 
    Def_Str = "<defs>" +CR_LF  +"  <path id=" +DQ  +Path_ID +DQ +" d=" +DQ +PathPoints +DQ +" "
    Def_Str &= FormatStyle(Path_Style, 0, 0, 1)  +"></path>" +CR_LF + "</defs>" +CR_LF 
    If ShowPath = 1 Then Def_Str &= "  <use href=" +DQ +"#" +Path_ID +DQ +"></use>" 
      Text_Str = "<text " +FormatStyle(Text_Style, 0, 0, 1)  +">" +CR_LF   
      Text_Str &= Indent1  +"<textPath  href=" +DQ +"#" +Path_ID +DQ +" startOffset=" +DQ +Str(Strt_Pos) +"%"">" +CR_LF
      Text_Str &= Indent1  +Indent1 +TXTstr +CR_LF
      Text_Str &= Indent1 +"</textPath>" +CR_LF
      Text_Str &= "</text>" +CR_LF
   Print #SVG_Fnum, Def_Str
   Print #SVG_Fnum, Text_Str  
End Sub


'   Quadratic Bezier curve (using path)
Sub SVG_Qbezier(X1 As Long, Y1 As Long, QX As Long, QY As Long, X2 As Long, Y2 As Long, ByVal StyleSVG As String="", RotateVal As Integer=0, StyleFmt As UByte=1)
   Dim As String ElemAttr1, TmpStr = ""
    ElemAttr1   = "M " +Str(X1) +"," +Str(Y1)  +" Q " +Str(QX) +"," +Str(QY) +" " +Str(X2) +"," +Str(Y2)
   If RotateVal <> 0 AndAlso InStr(LCase(StyleSVG), "rotate") = 0 Then  ' check if rotate set
      TmpStr = "Rotate=(" +Str(RotateVal) +"," +Str(X1) +"," +Str(Y1) +")" 
      If InStr(StyleSVG, "__") = 0 Then TmpStr &= "__" Else TmpStr &= " " ' check if left part exist
   EndIf
    StyleSVG = TmpStr +StyleSVG
    SVG_Path( ElemAttr1, StyleSVG, RotateVal, StyleFmt)
End Sub


'   Path Element
Sub SVG_Path(ByVal SVGpathStr As String, ByVal StyleSVG As String="", RotateVal As Integer=0, StyleFmt As UByte=1)
 Dim As String Indent1 ="", ElemAttr1 = "", ElemAttr2, ElementStr1, TmpStr
 Dim As Integer Pos1 = 0
    ElementStr1 = Indent1 +"<path d=" +DQ '+CR_LF 
      TmpStr = SVGpathStr
      Pos1 = InStr(TmpStr, CR_LF)
   Do While Pos1 <> 0          '  Indent path points if CRLF in PathString
      ElemAttr1 &= Indent1 +Left(TmpStr, Pos1-1)
      TmpStr = Right(TmpStr, Len(TmpStr)-(Pos1+1))
      Pos1 = InStr(TmpStr, CR_LF)
      Indent1 = CR_LF +Space(9)
      If Pos1 = 0 AndAlso Len(TmpStr) > 1 Then ElemAttr1 &= Indent1 +TmpStr' +CR_LF
   Loop 
    If TmpStr = SVGpathStr Then ElemAttr1 = SVGpathStr ' If no CRLF   
    ElementStr1 &= ElemAttr1 +DQ
   ElemAttr2 = FormatStyle(StyleSVG, 0, 0, StyleFmt) ' transform rotate is default ViewBox 0,0
   Print #SVG_Fnum, ElementStr1; CR_LF; ElemAttr2 +"/>" 
End Sub

 
'   Linear Gradient
Sub SVG_GradientL(Grad_ID As String, Color_1 As String, Color_2 As String, Stop1 As Integer, Stop2 As Integer, X1pc As Integer, X2pc As Integer, Y1pc As Integer, Y2pc As Integer)
   Dim As String Def_Str, Text_Str, Indent1 = "   "
    Def_Str = " <defs>" +CR_LF
    Def_Str &= Indent1 +"<linearGradient id=" +DQ + Grad_ID +DQ 
      Def_Str &= " x1=" +DQ +Str(X1pc) +"%" +DQ
      Def_Str &= " x2=" +DQ +Str(X2pc) +"%" +DQ 
      Def_Str &= " y1=" +DQ +Str(Y1pc) +"%" +DQ
      Def_Str &= " y2=" +DQ +Str(Y2pc) +"%" +DQ +">"  +CR_LF
    Def_Str &= Indent1 +Indent1 +"<stop offset=" +DQ +Str(Stop1) +"%" +DQ +" stop-color=" +DQ +Color_1 +DQ +" />" +CR_LF
    Def_Str &= Indent1 +Indent1 +"<stop offset=" +DQ +Str(Stop2) +"%" +DQ +" stop-color=" +DQ +Color_2 +DQ +" />" +CR_LF
    Def_Str &= Indent1 +"</linearGradient>" +CR_LF
    Def_Str &= " </defs>" +CR_LF
   Print #SVG_Fnum, Def_Str  
End Sub

'   DropShadow
Sub SVG_DropShadow(DS_ID As String, Color_1 As String, DX As Integer=10, DY As Integer=10, Blur As Single=2, feOffset As Integer= -20, feSize As Integer=180)
   Dim As String Def_Str, Text_Str, Indent1 = "   "
    Def_Str = " <defs>" +CR_LF
    Def_Str &= Indent1 +"<filter id=" +DQ + DS_ID +DQ 
      Def_Str &= " x=" +DQ +Str(feOffset) +"%" +DQ
      Def_Str &= " y=" +DQ +Str(feOffset) +"%" +DQ 
      Def_Str &= " width=" +DQ +Str(feSize) +"%" +DQ
      Def_Str &= " height=" +DQ +Str(feSize) +"%" +DQ +">"  +CR_LF   
    Def_Str &= Indent1 +Indent1 +"<feDropShadow" +" dx=" +DQ +Str(DX) +DQ  +" dy=" +DQ +Str(DY) +DQ _
                                +" stdDeviation=" +DQ +Str(Blur) +DQ +" flood-color=" +DQ +Str(Color_1) +DQ  +" />" +CR_LF
    Def_Str &= Indent1 +"</filter>" +CR_LF
    Def_Str &= " </defs>" +CR_LF
   Print #SVG_Fnum, Def_Str     
End Sub


'   GaussianBlur 
Sub SVG_GaussianBlur(DS_ID As String, DX As Integer=0, DY As Integer=0, Blur As Single=2, feOffset As Integer= -20, feSize As Integer=150)
   Dim As String Def_Str, Text_Str, Indent1 = "   "
    Def_Str = " <defs>" +CR_LF
    Def_Str &= Indent1 +"<filter id=" +DQ + DS_ID +DQ 
      Def_Str &= " x=" +DQ +Str(feOffset) +"%" +DQ
      Def_Str &= " y=" +DQ +Str(feOffset) +"%" +DQ 
      Def_Str &= " width=" +DQ +Str(feSize) +"%" +DQ
      Def_Str &= " height=" +DQ +Str(feSize) +"%" +DQ +">"  +CR_LF   
    Def_Str &= Indent1 +Indent1 +"<feGaussianBlur" +" stdDeviation=" +DQ +Str(Blur) +DQ +" result=" +DQ + DS_ID +DQ +" />" +CR_LF  
    Def_Str &= Indent1 +Indent1 +"<feOffset " +"dx=" +DQ +Str(DX) +DQ  +" dy=" +DQ +Str(DY) +DQ +" />" +CR_LF
    Def_Str &= Indent1 +"</filter>" +CR_LF
    Def_Str &= " </defs>" +CR_LF
   Print #SVG_Fnum, Def_Str     
End Sub

'   <defs>
'      <filter id="shadow1" x="-20%" y="-20%" width="140%" height="140%">
'        <feGaussianBlur stdDeviation="1.5" result="shadow1"/>
'	      <feOffset dx="-6" dy="6"/>
'      </filter>
'   </defs>


Sub SVG_Image(X As Long, Y As Long, W As Long, H As Long, ByVal KeepAspect As String="", ByVal ImgPathFile As String, RotateVal As Integer=0, StyleFmt As UByte=1)
   Dim As String Indent1, ElemAttr1, ElementStr1, ElemAttr2, TmpStr
   ElementStr1 = Indent1 +"<image href=" +DQ
   If InStr(ImgPathFile,"http") = 0 Then ' local file
      ElementStr1 &= "file:" 
      If InStr(ImgPathFile,":") <> 0 Or InStr(ImgPathFile,"\") <> 0 Then ElementStr1 &= "//" ' with full path    
   End If
   ElementStr1 &= ImgPathFile +DQ
   ElemAttr1 = " x=" +DQ +Str(X) +DQ +" y=" +DQ +Str(Y) +DQ +" width=" +DQ +Str(W) +DQ +" height=" +DQ +Str(H) +DQ
   If RotateVal <> 0 Then ElemAttr2 = " transform=""rotate(" +Str(RotateVal) +"," +Str(X+W/2) +"," +Str(Y+H/2)  +")" +DQ
    ElementStr1 &= ElemAttr1                                 ' Rotate replaces x y with a translate(x,y)
   If KeepAspect <> "" Then ElemAttr2 &= " preserveAspectRatio=" +DQ +Trim(KeepAspect) +DQ     'FormatStyle(StyleSVG, X, Y, StyleFmt) 
    Print #SVG_Fnum, ElementStr1; ElemAttr2; TmpStr; "/>"
End Sub
    ' ref
   '<image href="file:mdn_logo_only_color.png" x="550" y="280" width="150" height="200"
   '<image href="file://d:\Data\FBedit\DataGraph\mdn_logo_only_color.png" x="600" y="300" width="150" height="150"/>
   '<image href="https://i.postimg.cc/qqT64fy9/mdn-logo-only-color.png" x="550" y="280" width="150" height="200"



'                  Misc. Subs
'----------------------------------------------------------
Sub SVG_Comment(TXTstr As String, LFCR As UByte=0)
    If LFCR > 0 Then Print #SVG_Fnum, ""
    Print #SVG_Fnum, "<!-- "; TXTstr; " -->"
End Sub

Sub SVG_CRLF(n_ As Ubyte =1)
    Dim As UByte Lp1
    For Lp1 = 1 To n_ : Print #SVG_Fnum, : Next
End Sub

  ' make SVG line from TextIn (must be Valid SVG syntax string)
Sub SVG_RAW(TextIn As String)
    Print #SVG_Fnum, TextIn
End Sub


' Find un-escaped "&" & escape it 
Function FindAndChr(String1 As String) As String
   Dim As Integer Pos1 = 0
   Dim As String TempStr = "", EscAnd = "&#38;"  '"&amp;"
     Pos1 = InStr(String1,"&")
     If Pos1 = 0 Or Mid(String1,Pos1,5) = EscAnd Then ' no "&" or it escaped
        TempStr = String1
     Else
        TempStr = Left(String1,Pos1-1) +EscAnd +Right(String1, Len(String1)-Pos1)   
     EndIf
   Function = TempStr
End Function



'                  Utility Functions
'---------------------------------------------------------
'        Get the XY Center & XY Min Max for polygon points
Function SVG_GetPolyMid(byVal PolyPts As String, PolyBBox() As Long) As Integer 
   Dim As UInteger Lp1, MaxVal_X, MaxVal_Y, MinVal_X = &hFFFFFF, MinVal_Y = &hFFFFFF
   Dim As String Chr1, ValStr, Xval, Yval   
   Xval = "" : Yval = ""

   For Lp1 = 1 To Len(PolyPts)   
         Chr1 = Mid(PolyPts, Lp1,1)
      If (Chr1 <> " " AndAlso Chr1 <> ",") Then ValStr &= Chr1   ' get the number chr's (only chr(32) whitespace allowed)
      If Lp1 = Len(PolyPts) Andalso ValStr <> "" Then chr1 = " " 
       ' Get X val
    	If Chr1 = ","  AndAlso Len(ValStr) > 0 Then   
         Xval = ValStr : ValStr = "" ': XvalFound =  TRUE
         If MaxVal_X < Val(Xval) Then MaxVal_X = Val(Xval)
         If MinVal_X > Val(Xval) Then MinVal_X = Val(Xval)
         Yval = ""
    	EndIf
       ' Get Y val         
      If Chr1= " " AndAlso Len(ValStr) > 0 AndAlso Len(Xval) Then' AndAlso Xval > 0 Then            ' Get Y val
         Yval = ValStr : ValStr = "" ': YvalFound =  TRUE
         If MaxVal_y < Val(Yval) Then MaxVal_Y = Val(Yval)
         If MinVal_y > Val(Yval) Then MinVal_Y = Val(Yval)
         Xval = ""
      EndIf
   Next
   '           Array Return ByRef - Bounding Box
   PolyBBox(1) = MinVal_X + ((MaxVal_X - MinVal_X) / 2) ' Mid X
   PolyBBox(2) = MinVal_Y + ((MaxVal_Y - MinVal_Y) / 2) ' Mid Y
   PolyBBox(3) = MinVal_X : PolyBBox(4) =  MaxVal_X       ' MinMax X
   PolyBBox(5) = MinVal_Y : PolyBBox(6) =  MaxVal_Y       ' MinMax Y
End Function


' ---------- Warning: Dont mix Style Left & Right parts when calling these 2 Functions ---------
' Convert separate style strings from program to single style string. (might be useful)
' use for inserting variables in style string.
'       Return Style string from "String" +Str(Value),"String" +Str(Value), ...
Function SVG_StyleRet1(ByVal AttVal_1 As String="", ByVal AttVal_2 As String="", ByVal AttVal_3 As String="", ByVal AttVal_4 As String="", ByVal AttVal_5 As String="") As String
    Dim As String AttributeVal(1 to 6) = {AttVal_1, AttVal_2, AttVal_3, AttVal_4, AttVal_5,""}
    Dim As String StyleSVG = ""
    Dim As UByte Lp1
   For Lp1 = 1 To 5
      If Lp1 <> 1 AndAlso AttributeVal(Lp1) <> "" Then StyleSVG &=  ", "
      If AttributeVal(Lp1) <> "" Then StyleSVG &= AttributeVal(Lp1)
   Next   
    SVG_StyleRet1 = StyleSVG 
End Function

 '       Return Style string from "Str",Valu,"Str",Valu, ...     (might be useful)
Function SVG_StyleRet2(ByVal AttName_1 As String="", ByVal AttValu_1 As Single=0, ByVal AttName_2 As String="", ByVal AttValu_2 As Single=0, _
                       ByVal AttName_3 As String="", ByVal AttValu_3 As Single=0, ByVal AttName_4 As String="", ByVal AttValu_4 As Single=0) As String
    Dim As String AttributeName(1 to 4) = {AttName_1, AttName_2, AttName_3, AttName_4}
    Dim As Single AttributeValu(1 to 4) = {AttValu_1, AttValu_2, AttValu_3, AttValu_4}
    Dim As String StyleSVG = ""
    Dim As UByte Lp1
   For Lp1 = 1 To 5
      If Lp1 <> 1 AndAlso AttributeName(Lp1) <> "" Then StyleSVG &=  ", "
      If AttributeName(Lp1) <> "" Then StyleSVG &= AttributeName(Lp1) + Str(AttributeValu(Lp1))' +", " 
   Next
    SVG_StyleRet2 = StyleSVG 
End Function
'------------------------------------------------




'         Formatting Attributes & Values for Transform & Style
'=====================================================================
'       Separate Transform & Style, format each then return formatted string
Private Function FormatStyle(ByVal Style As String, ByVal Xs As Integer = 0, ByVal Ys As Integer = 0, ByVal StyleFmt As Ubyte = 0) As String
   Dim As UByte Pos1, Part
   Dim As String TransF, Style1, TmpStr
       'Style = 
      Pos1 = Instr(Style , "__") 
      If Pos1 > 0 Then
         TransF = left(Style, Pos1-1)             ' Transform part
         Style1 = Right(Style, (Len(Style)-(Pos1+1))) ' Style part
         Part = 1 : TmpStr =  FormatStyle2(TransF, Xs, Ys, 1, Part)          ' Transform(& other) part
      Else
         Style1 = Style   
      EndIf
         Part = 2 : TmpStr & = FormatStyle2(Style1, Xs, Ys, StyleFmt, Part)  ' Style part
      FormatStyle = TmpStr
'Print #5, "Style ret L+R--"; TmpStr ' DEBUG
End Function 


   Const LB = "(" : Const RB = ")"
'       Format Style (or Element attributes)  
Private Function FormatStyle2(ByVal StyleSVG As String, ByVal Xs As Integer = 0, ByVal Ys As Integer = 0, _
                              ByVal StyleFmt As UByte = 1, Byval Part As Ubyte) As String
   Dim As String Chr1, TmpStr, Attr_Add, TranslateOriginPart
   Dim As UByte BrktL, CSF1, CSF2, StrLen, Lp1, Lp2, Pos1, PartCnt1
   Dim As String StylePart(20,3), RotatePart, SkewXpart, SkewYpart, ScalePart, TranslatePart, MatrixPart
      If StyleSVG = "" Then Exit Function
      BrktL = 0 : CSF1 = 1 : CSF2 = 1 : PartCnt1 = 1
    ' Find CS Values (CSV) 
      StrLen = Len(StyleSVG)
   For Lp1 = 1 To StrLen   
      Chr1 = Mid(StyleSVG, Lp1,1)
      If Chr1 = "(" Then BrktL = Lp1                                  ' 
      If Chr1 = ")" AndAlso BrktL > 0 Then BrktL = 0                  '
      If (Chr1= "," Andalso BrktL = 0) Then CSF2 = LP1                ' Ignore "," between brackets
      If Lp1 = StrLen And Right(StyleSVG,1) <> "," Then CSF2 = LP1+1  ' Get the last CS Val 
         
    	If CSF2 > CSF1 Then
         TmpStr = Trim(Mid(StyleSVG, CSF1, CSF2-CSF1))  ' Extract CSV data
         Pos1 = Instr(TmpStr , "=")
       ' Store style in array
#IfDef L_case                   ' Attrib part
         StylePart(PartCnt1,1) = LCase(left(TmpStr, Pos1-1))       ' Force Lcase left of =
#Else
         StylePart(PartCnt1,1) = left(TmpStr, Pos1-1)              ' MixedCase
#EndIf
         StylePart(PartCnt1,2) = Right(TmpStr, (Len(TmpStr)-Pos1)) ' Value part
          TmpStr = ""
          PartCnt1 +=1
          CSF1 = CSF2+1 
      EndIf
   Next

      If Part = 1 Then  ' Format Element values or Transform="    "
         If PartCnt1 < 1 Then Exit Function
'Print #5, "StyleSVG In=" +StyleSVG  ' DEBUG
            Attr_Add = ""
         For Lp1 = 1 To PartCnt1-1
              'Remove value Brakets
               Pos1 = InStr(StylePart(Lp1,2),"(")
               If Pos1 > 0  Then Mid(StylePart(Lp1,2), Pos1,1) = " "
               Pos1 = InStr(StylePart(Lp1,2),")")
               If Pos1 > 0  Then Mid(StylePart(Lp1,2), Pos1,1) = " "' : StylePart(Lp1,2) = Trim(StylePart(Lp1,2))
               StylePart(Lp1,2) = Trim(StylePart(Lp1,2)) 
 
'Print #5, "Part1_Len= " +Str(Len(StylePart(Lp1,2))) ' DEBUG
'Print #5, "Part1_2= " +StylePart(Lp1,1); " _ " +StylePart(Lp1,2) : #EndIf' DEBUG

               'TmpStr = StylePart(Lp1,1)
               TmpStr = LCase(StylePart(Lp1,1))
            Select Case TmpStr 'StylePart(Lp1,1)               
               Case Is = "scale"
                  ScalePart = "translate(" +Str(Xs) +"," +Str(Ys) +") " _
                                 +"scale(" +Str(StylePart(Lp1,2)) +") " _
                             +"translate(" +Str(-Xs) +"," +Str(-Ys) +") "
               Case Is = "rotate"
                  RotatePart = "rotate(" +StylePart(Lp1,2) +"," +Str(Xs) +"," +Str(Ys) +") "
                  If InStr(StylePart(Lp1,2)," ") OrElse InStr(StylePart(Lp1,2),",") Then ' rotate point is in rotate val
                     RotatePart = "rotate(" +StylePart(Lp1,2) +") "
                  EndIf  
               Case Is = "skewx"
                  SkewXpart = "skewX(" +StylePart(Lp1,2) +") " 'must be Ucase "X"
               Case Is = "skewy"
                  SkewYpart = "skewY" +StylePart(Lp1,2) +") "  '      "       "Y"
               Case Is = "matrix"
                  MatrixPart = "matrix(" +StylePart(Lp1,2) +") "   ' not tested yet 2024.01.06
               Case Is = "translate"
                  TranslatePart = "translate(" +StylePart(Lp1,2) +") "
               Case Is = "transform-origin"
                  TranslateOriginPart = "transform-origin" +StylePart(Lp1,2) ' Not working yet, puts inside main " "
                 
               Case Else ' Non style attributes, but not part of Transform="    " 
'Print #5, "Not Transform attr = " +TmpStr 'DEBUG
                  If StylePart(Lp1,1) = "rx" Then Attr_Add &= " rx=" +DQ +Str(StylePart(Lp1,2)) +DQ
                  If StylePart(Lp1,1) = "ry" Then Attr_Add &= " ry=" +DQ +Str(StylePart(Lp1,2)) +DQ
               '     For additional element attributes, add them here...
            End Select
         Next
                    ' Concatenate in order  [SVG Transform evaluates Left <-- Right]
            TmpStr = ""
            TmpStr= TranslatePart +RotatePart +TranslateOriginPart +SkewXpart +SkewYpart +ScalePart +MatrixPart 
            If TmpStr <> "" Then TmpStr= " transform=" +DQ +Trim(TmpStr) +DQ
            
            FormatStyle2 = Attr_Add +TmpStr
         Exit Function                '< already formatted (is fixed type)
      EndIf

   '         Build style string       'Look at using following code block with transform=... ? (Part = 1)
            StyleSVG = "" : PartCnt1 -=1 
   For Lp1 = 1 To PartCnt1
'Print #5, "STpart_2= " +StylePart(Lp1,2)
      
'     Enable use of brackets or "," in Dasharray value input eg. (3,5) or (3 5) or 3 5
      Chr1 = "" : TmpStr = ""
      If InStr(StylePart(Lp1,1), "stroke-dasharray") AndAlso InStr(StylePart(Lp1,2), "(") Then
         For Lp2 = 1 To Len(StylePart(Lp1,2))
            Chr1 = Mid(StylePart(Lp1,2),Lp2,1)
     	      If Chr1 = "," Then Chr1 = " " 
     	      If Chr1 = ")" Then Exit For  ' we are done
     	      If Chr1 <> "(" Then TmpStr &= Chr1  
         Next
         StylePart(Lp1,2) = TmpStr
      EndIf

'     Format style
      Select Case StyleFmt
         Case 0 ' [<g  >] Group style format (each attribute on newline) 
            StyleSVG &= "   " + StylePart(Lp1,1) + "=""" + StylePart(Lp1,2) + """" '+ CR_LF
            If Lp1 <> PartCnt1 Then StyleSVG &= CR_LF 
         Case 1 ' [: ;] style format
            If Lp1 = 1 Then StyleSVG = " style="""
            StyleSVG &= StylePart(Lp1,1) + ":" + StylePart(Lp1,2) 
            If Lp1 <> PartCnt1 Then StyleSVG &= ";" : Else StyleSVG &= """"  :EndIf
         Case 2 ' [=" "] DQ style format
            StyleSVG &= " " +StylePart(Lp1,1) + "=""" + StylePart(Lp1,2) + """"
      End Select
   Next
      FormatStyle2 = StyleSVG
End Function

#Undef CR_LF
#UnDef DQ

'   ---------END-------------------

Test ezySVG_1.bas

Code: Select all

'   TestSVG_1.bas  - Freebasic Code

'Example for ezySVG Library
'   This code is just a list of examples to show usage, it's not meant to be a structured program.
'   Makes SVG in the app run folder.  
'   To see result, click on the output .html file or drop on browser. 'View Page Source' to check/debug SVG code 
'   All drawing is done in the SVG ViewBox that sits on the virtual canvas default top left, the size & offset is set in  'SVG_Start'
'   The SVG contains Elements (Line, rect, circle etc...) & they each have parameters(values) for position, size etc.
'   Elements also have other attributes & style attributes. 

'     Many style attributes are optional & often have a default value (some may result in an Element part not rendered.) 
'     Be mindful tha not all SVG render apps are equal, what works in 1 may not in another.

'    To set style attributes you refer to SVG style reference, same when using named colors (CSS)
'    Typically all Style attributes are lower case, however you may use first letter Ucase for clarity but a few need to be case sensitive
'    such as stroke-dasharray must be Lcase(tested in fireFox), some have Ucase parts. Just lookup a reference to be sure.

'     Most values can have space or comma separator,     stroke=rgb(130 130 130) stroke=rgb(130,130,130) <- both are valid in ezySVG lib
'     Only parameters with more than 1 value have brackets &  "," or " " separators.
'     with the exception of stroke-dasharray=(3,5)  or =(3 5) or =3 5 
'		Rotate(in style string) has 2 modes Rotate=-12 uses default Element point Rotate=(-12,100,220) uses points set.                      

'   Sometimes when drawing an Element or making changes, it seems to disapear but it could just be outside the viewbox
'   somewhere on the canvas (canvas has no size limit) or there is a typo.

' Color values(RGB) are string either  #C0FFE9 or RGB(255,89,130)
' Element position & size values are number(long), Element attributes are string, Style is a string
' Style string has optional left part (non style attributes) with separator "__"(Double Underscore). 
' These are for Transform & additionakl Element attributes.

'   V0.90 2024-02-15   

#Define L_Case       ' Use to turn on Lcase formatting of SVG Style Names (must be before #Include "ezySVG.bi")
#Include "ezySVG.bi"
 
 Dim As String  TempStr, StyleStr
 Dim As UInteger VboxSizeX, VboxSizeY, Lp1
 Dim As Uinteger FillCol, LineCol, LineW, X, Y
 Dim As String SVGstyle, HexRGB1, HexRGB2
   Dim As string PolyPoints, PathPts, TxtStr
   Dim As Integer PolyMid(1 To 10) ' polygon virtual bounding box(MinMax & CTR)
   Dim As Integer XS, YS, Rote, SkewXval, SkewYval

'  Open "debug2.txt" For Output As #5

   VboxSizeX = 1000
   VboxSizeY = 500
Const CR_LF = Chr(13,10)
Const    DQ = Chr(34)
Const html_ = "html" ' choose "svg in html"  
'Const html_ = ""    '      or "svg"
      
'      SVG_Start("TestSVG_1.SVG", VboxSizeX, VboxSizeY, -500, -250, "html") ' with offset
      SVG_Start("TestSVG_1.SVG", VboxSizeX, VboxSizeY, 0, 0, html_)
     'SVG_Start("TestSVG_1.SVG", VboxSizeX, VboxSizeY, 0, 0)
'      SVG_Rect(-20, -20, 20, 20, "rx=5, ry=5_fill=silver, stroke=black, stroke-width=2")   ' test offset

       ' Rectangle to fit SVG ViewBox
       StyleStr = "Fill=Silver, stroke=black, stroke-width=2"', fill-opacity=1, stroke-opacity=1"
       SVG_Comment("   This rect fills the ViewBox", 1)
       SVG_Rect(0, 0, VboxSizeX, VboxSizeY, StyleStr) 
        SVG_CRLF  '<- put a newline in SVG code to improve structure (no effect on graphic)
       SVG_Line(VboxSizeX/2, 0, VboxSizeX/2, VboxSizeY, "stroke=rgb(130 130 130), stroke-width=1, stroke-dasharray=(8,12)")
       SVG_Line(0, VboxSizeY/2, VboxSizeX, VboxSizeY/2, "stroke=rgb(130,130,130), stroke-width=1, stroke-dasharray=(8 12)")
        SVG_CRLF
       ' Text at ViewBox X centre
       TempStr = "ezySVG Test  2024.02.11"
       SVG_Text(VboxSizeX/2, 25, TempStr,  "font-family=verdana, font-size=18px, Font-Weight=bold, fill=navy, text-anchor=middle",,1)
      ' various Text styles
      SVG_Text(25, VboxSizeY/2, "Rotate Left (middle anchor)", "text-anchor=middle",-90)
      SVG_Text(VboxSizeX-25, VboxSizeY/2, "Rotated Right (middle anchor)", "font-family=verdana, fill=purple,  text-anchor=middle",90)   
      SVG_Text(520, 75, "Text - Outline only", "font-family=verdana, font-size=24px, Font-Weight=bold, Fill=none, Stroke-width=1, stroke=black")
      SVG_Text(10, VboxSizeY-10, "If viewing this graphic in a web browser, view page source to see SVG code(syntax color'd)", "font-style=italic ,font-size=12px")
      SVG_Text(1,10, "0,0", "Font-style=italic ,font-size=12px")
      SVG_Text(501,260, "500,250", "Font-style=italic ,font-size=12px")      
       SVG_CRLF
      SVG_Comment(" some lines...")
      ' 2nd line is rotated 5deg.
      SVG_Line(10, 40, 200, 40, "stroke=green, stroke-width=2")   
      SVG_Line(600, 40, 700, 40, "stroke=green, stroke-width=2",5) 

'-----------------------------      
      ' Apply style to a group  
      SVG_Comment("   This is a style group", 1)
      StyleStr = "stroke=blue, stroke-width=2, stroke-dasharray=(8,2)"   
      SVG_Group(StyleStr) ' group start
         For Lp1 =  60 To 100 Step 10
            If Lp1 = 80 Then StyleStr = "Stroke=Purple, stroke-width=3, stroke-dasharray=0" : Else StyleStr = "" 
            SVG_Line(10, Lp1, 200, Lp1, StyleStr,,0)  ' set the Style format "0" adds indent
         Next
      SVG_Line(10, 110, 200, Lp1,"rotate=5, scale=1.5__Stroke=yellow, Stroke-Width=1, stroke-dasharray=(3,5)",,0)' override group style for this element
      SVG_Group("End")           ' group close - End or Null param   

      SVG_Text(10, 125, "6 lines above are in a style group,  2 lines have style override", "font-size=12px",5)
      SVG_Line(230, 40, 475, 90, "stroke=red, stroke-width=5") 

'-----------------------------
      ' Rectangle  rounded corners & fill transparancy 60%
      StyleStr = "fill=#C0E2D5, stroke=black, stroke-width=2, fill-opacity=0.6"' , stroke-opacity=1"
      SVG_Rect(250, 40, 200, 50, StyleStr)
      
      
      LineW = 1 : LineCol = 24000  ' LineCol might come from a color map
      FillCol = 764345
      HexRGB1 = "#" + Hex(FillCol,6)
      HexRGB2 = "#" + Hex(LineCol,6)                                  '                
'Print #5, "FillCol "; FillCol; "  "; HexRGB1      
      SVG_Ellipse(80, 180, 25, 15, "fill=#E2C0D5, Stroke=RGB(00,175,255), Stroke-Width=" +Str(LineW+2) )
      SVG_Ellipse(80, 220, 25, 15, "fill=#E2C0D5, Stroke=RGB(00,75,255), Stroke-Width=2",45)     ' rotated 45deg
      SVG_Circle(160, 200, 25, "fill=#E2C0D5, Stroke=RGB(00,00,255), Stroke-Width=3")
      SVG_Circle(200, 200, 30, "fill=none, stroke=black, stroke-width=2, Stroke-opacity=0.5")    ' outline only
      SVG_CRLF


'-------------------------------------------------------      
      ' These lines have variables for each attribute value   
            SVGstyle = "stroke=" +HexRGB2 +", Fill=" +HexRGB1 +", stroke-width=" +Str(LineW)
      '     SVGstyle = "stroke=#" +Hex(LineCol,6) +", Fill=#" +Hex(FillCol,6) +", stroke-width=" +Str(LineW)
      
      '------------------------------------------------------
      ' Using SVG_StyleRet1() may be easier...
      ' !!! Dont mix Style Left__right parts
      SVGstyle = SVG_StyleRet1("stroke=" +HexRGB2, "Fill=" +HexRGB1, "stroke-width=" +Str(LineW))
      'SVG_Line(45, 373, 500, 373, SVGstyle)
      SVG_Rect(950, 45, 30,8, SVGstyle)    
      
      ' Using SVG_StyleRet2() may be easier... but not many attributes
      ' !!! Dont mix Style Left__right parts
'      SVGstyle = SVG_StyleRet2("Rotate=", Rote, "Skew=", SkewXval, "stroke-width=", LineW, "Stroke-Opacity=", 0.6)
      SVGstyle = SVG_StyleRet2("stroke-width=", LineW, "Stroke-Opacity=", 0.6)
      SVG_Line(45, 371, 500, 371, SVGstyle +", stroke=red")
      SVG_Line(45, 372, 500, 372, SVGstyle +", stroke=green")  '< Note the comma in string for additional style
      SVG_Line(45, 373, 500, 373, SVGstyle +", stroke=blue")
      '------------------------------------------------------
      
      SVG_Text(950, 30, " Some tiny text to see", "font-size=3px") ' SVG does not do multiline text !!! (CRLF not work) 
      SVG_Text(950, 35, " Zoom in to read","font-size=3px")   
      SVG_Text(950, 40, " SVG Doesnt do MultiLine Text!","font-size=3px")

      Rote = 20
   PolyPoints = "350,100 379,181 469,161 397,215 423,301 350,250 277,301 303,215 231,161 321,181"
   SVG_Polygon(PolyPoints, "fill=none, stroke=red, stroke-width=1, stroke-dasharray=(2,3)")
   SVG_Polygon(PolyPoints, "fill=none, stroke=blue, stroke-width=1, stroke-dasharray=(4,8)",Rote)
   SVG_Polygon(PolyPoints, "Rotate=-12__fill=none, stroke=green, stroke-width=1, stroke-dasharray=(4,4)") ',Rote-8
   'Polygon & PolyPoints are very similar. PP need extra point to close, Both need fill=none for line only.
   SVG_Text(350, 340, "PolyGon (rotated)", "font-size=13px")
   
   SVG_Comment("   Poly Line", 1)
   'PolyLine
   SVG_PolyLine("300,400 350,450 400,400 300,400", "Rotate=(2,350,450)__stroke=blue, stroke-width=2")
   '                                                In above fill is not specified so default is fill=black 
   '                                                Rotate origin is specified
   SVG_PolyLine("300,400 350,450 400,400 300,400", "Rotate=90__stroke=yellow, stroke-width=1, fill=none")
   '                                                Rotate origin is not specified (uses polypoints xy mid)
   SVG_Text(400, 465, "PolyLine", "font-size=13px")
   
   '  Testing PolyMid
      SVG_GetPolyMid(PolyPoints, PolyMid()) ' get polygon/polyline centre & XY min/max (bounding box)
      XS = 40 : YS = 40 
      SVG_Rect(PolyMid(1)-XS/2, PolyMid(2), XS, YS, StyleStr, 45) '< put a rectangle in the polygon centre
      SVG_Circle(PolyMid(1), PolyMid(2), 5, "fill=none, stroke=black, stroke-width=1, Stroke-opacity=0.9")
   
   SVG_Comment("   Quad Bezier curve",1 )  ' 3 points (X1, Y1, X2, Y2, QX, QY)
'/'
 Rote = 15
   StyleStr = "stroke=white, stroke-width=3, fill=none"
   SVG_Qbezier(520, 150, 560, 30, 620, 150, "stroke=black, stroke-width=2, fill=purple, Fill-opacity=0.4", Rote)
   SVG_Qbezier(520, 150, 570, 150, 620, 150, StyleStr, Rote)
'/   
   
'   SVG_Qbezier(520, 150, 560, 30, 620, 150, "stroke=black, stroke-width=2, fill=purple, Fill-opacity=0.4")
'   SVG_Qbezier(520, 150, 570, 150, 620, 150, "Rotate=(12,520,150)__stroke=white, stroke-width=3, fill=none")
   
              ' Str(Y2-(Y2-Y1/2)
''SVG_Path("M620,150 Q650,70 680,150" ,StyleStr) ' see Test Qbezier.bas

   SVG_Text(520, 190, "Quad Bezier curve (1 control point)", "font-size=13px")
   SVG_Text(520, 205, "Rotated 15deg. with fill &#38; Baseline", "font-size=13px") ' Note: &#38; = "&" (& is illegal chr)
   
   SVG_CRLF
   SVG_Rect(700, 100, 80, 40, "rx=8, ry=8__fill=darkblue, stroke=green, stroke-width=3") ' Note the radius attributes 
   SVG_Rect(800, 100, 80, 40, "rx=8, ry=8, SkewX=10__fill=none, stroke=black, stroke-width=1") '  in Transform part (left of separator "__")
   
   SVG_Comment("SVG Paths", 1) ' SVG Path (point values would typically be produced from geometric/math code)
   PathPts =    "  M 213,7" +CR_LF _
               +"  c -32-14-74,0-88,30" +CR_LF _
               +"  C 110,5,67-9,36,6" +CR_LF _
               +"  z"

      PathPts = "M 213,7" +" c -32 -14 -74,0 -88,30" +"  C 110,5,67 -9,36,6" +"  z" ' z = line to start point
   SVG_Path(PathPts, "Fill=none, Stroke=black")
    
   '   This path has the same xy points but is translated(moved) to Y= 420 * has rotate applied
   SVG_Path("M 213,7 c -32 -14 -74,0 -88,30 C 110,5,67 -9.5,36,6", "Rotate=(15,213,7), translate=(0,440)__Fill=none, Stroke=blue")
   SVG_Text(50, 465, "Path(2x C.Bezier curve)", "font-size=13px")
   ' Path points are like polyline & polygon points in that the previous segment end point is the next segment start point.
   ' this can be a bit hard to "see" the segment values when the type changes(line to bezier) especially Cubic beier.
   ' In above there are 3 line segments followed by 2 Quadratic(1 ctrl pt) bezier curves.
   ' Typically Ucase type(M L Q) is explicit point & Lcase type(m l q) is relative to previous point.
   
   '                                        BZstrt    BZctrl  BZEnd
   '               Moveto   Lineto  Lineto  Lineto            BZstrt  BZctrl  BZEnd
   PathPts = "M 50,300 L 100,300 120,325 150,325 Q 175,300 200,325 225,350 250,325" 'z"   (z is line to start)  
   SVG_Path(PathPts, "Fill=none, Stroke=black")
   SVG_Text(50, 355, "Path(3x lines, 2x Qbezier curve", "font-size=13px")
   SVG_Path("M 500,220 L 500, 280 M 470,250 L 530,250", "Fill=none, Stroke=black") ' crosshairs
   SVG_Path("M 100,400 Q 175,375 200,400 175,425 100,400", "Fill=none, Stroke=blue")
   SVG_CRLF
   
   '                     preserveAspectRatio xMinyMin, xMaxyMax not working !
   ' Raster Image
   SVG_Comment("    Raster Image in SVG",1)
   Dim As String FilePath = "" '"d:\Data\FBedit\DataGraph\"  ' full path
   Dim As String FileName
 ' choose your image source Filename (local or online)
'      FileName = "mdn_logo_only_color.png"                               ' file name only (img in Test ezySVG_1exe run folder)
     FileName = "https://i.postimg.cc/qqT64fy9/mdn-logo-only-color.png" ' online res
   SVG_Rect(550, 280, 150, 200, "Fill=none, Stroke=grey, stroke-dasharray=(1,3)") ' Just for ref.
   SVG_Image(550, 280, 150, 200, , FilePath +FileName, 0) 'Param 5: KeepAspect is default, or "none" to fit XY 
   SVG_Text(550, 475, "Raster image", "font-size=13px")
   

   ' Text on Path
   TxtStr = "Text Along a Path - Cool" 
   SVG_Comment("    Text on a Path",1)
   PathPts = "M 700,325 Q 750,300 800,325 850,345 900,325"
   SVG_TextPath("Path1" , PathPts, "Fill=none, Stroke=blue, stroke-width=1, stroke-dasharray=(2,2)", TxtStr, "font-size=17px",8)
   
   ' Drop Shadow
   SVG_Comment("    Drop Shadow effect",1)
   SVG_DropShadow("DSf1", "grey", 5, 10, 2)  
   SVG_PolyLine("700,230 750,230 770,210 728,210 700,230", "filter=url(#DSf1), stroke=brown, stroke-width=1, fill=navy, fill-opacity=0.75")
   
   ' GaussianBlur
   SVG_Comment("    GaussianBlur effect",1)   
   SVG_GaussianBlur("GB1", 2, 2, 0.35) '(filter ID, Xoffset, Yoffset, blur)
   ' applied to a text Element to create DropShadow
   SVG_Group("text-anchor= middle, font-size=24px font-weight=bold, font-family=verdana")
    SVG_Text(820, 270, "TextShadow using GaussianBlur", "filter=url(#GB1), fill=#505050",,0) '< This is the shadow
    SVG_Text(820, 270, "TextShadow using GaussianBlur", "fill=navy",,0)     
   SVG_Group
'           Alternately we can set filter X Y offset=0 & position the shadow text

   ' Linear Gradient
   SVG_Comment("    Linear Gradient Fill",1)
   SVG_GradientL("Grad1", "blue", "red", 0, 100, 0, 100, 0, 0)
   SVG_DropShadow("DSf1", "grey", 12, 12, 1.5)
   SVG_Ellipse(850, 400, 100, 50, "fill=url(#Grad1), filter=url(#DSf1)",,2)
   SVG_Circle(850, 400, 30, "fill=url(#Grad1), stroke=black, stroke-width=2, Stroke-opacity=0.5",,2) 
   ' The chr case for ID name must match. ie: "Grad1" set & use "ul(#grad1)" will fail.
   
'   SVG_Text(750, 475, "Gradient Fill &#38; Drop Shadow", "font-size=13px")
   SVG_Text(750, 475, "Gradient Fill & Drop Shadow", "font-size=13px")
   SVG_Text(825, 200, "skewX Transform", "font-size=13px")
      
   SVG_CRLF
   'SVG_End("html")
   SVG_End(html_)
   'SVG_End()
   
 '  Beep   ' I am done  
 
' Close #5  '< DEBUG


End

' These 2 lines result in same SVG code
'     SVG_Line(600, 40, 700, 40, "stroke=green, stroke-width=2", 5)   or
'     SVG_Line(600, 40, 700, 40, "Rotate=5__stroke=green, stroke-width=2") note the underscore separator before style attributes.
   

=============================================
Last edited by Tonigau on Feb 17, 2024 8:54, edited 5 times in total.
Tonigau
Posts: 36
Joined: Feb 25, 2021 20:19

Re: ezySVG Lib for Freebasic - make SVG files

Post by Tonigau »

Preliminary Help for ezySVG, Just load into IDE, Not for compile.

TestingSVG Cmds.bas

Code: Select all

Help file only, Dont compile

'   TestingSVG Cmds.bas  - Freebasic
'ezySVG.bi   Reference only,   --* This code NOT for compile. *-- 

'For a full list of Style, Transform & SVG Element detail consult SVG reference documentation.
'SVG specification is vast.
'ezySVG is a limited implementation of SVG file, it is designed to be flexible to generate SVG.
'You can specifically generate SVG or shadow existing freebasic (eg. window9 graphic commands) to make a vector 'printout'.
'The lib does not error check syntax for SVG_Commands Transform & Style etc, usual result is non rendered Element.  
' Style names must be SVG syntactically correct eg. stroke-width=10, fill=none.
' Ucase may can be used for most style Names & Attributes (but some such as stroke-dasharray is case sensitive.(fireFox anyway))
' The Path Element string uses Upper/Lower case for different meaning.(relative or Absolute) preserveAspectRatio xMidyMid to name a few are case sensitive.
' Also skewX, skewY & linearGradient  are case sensitive - but we don't need to use these directly(lib handles these). 
' The lib will convert case where needed Stroke=Blue to stroke "blue" or stroke:blue   

' Note: Behavior of SVG rendering may depend on the application used & could vary from what is stated here.
'       eg fill not specified, stroke color not specified etc...
'       You might see highlight color in browser for syntax error in 'ViewPage Source', I used this when debugging the lib.
'       Also drop th .svg file onto firefox will give specific error if it is significant (not a style case error)  

'Commands  [ ] = optional parameter for ezySVG lib.
Const CR_LF = Chr(13,10)
#Define L_Case       '< Use to turn on Lcase formatting of SVG Style Names (must be before #Include "ezySVG.bi")
#Include "ezySVG.bi"
'SVG_Start()
   'This sets the SVG size, ViewBox & Any offset. [hlml] encloses SVG in html for 'open with browser'
   SVG_Start("TestSVG_1.SVG", 1000, 500, 0, 0, "html")   'FileName, Xs, Ys, [Xo], [Yo], [file = FileName.html], [xmlns string]
   SVG_Start("TestSVG_1.SVG", 1000, 500, 0, 0, "html", "xmlns=""http://www.w3.org/2000/svg""") '< with optional xmlns string 
   ' Save file where app run or include path in FileName.

'SVG_End
   ' Finalize the SVG file.
   SVG_End("html") '[html]
   
'SVG_Group 
   'Sets group Start or End
   SVG_Group(StyleStr) 'Style set = Start, "End" or NULL = End,
   SVG_Group("End") or SVG_Group() 'or any string less than 6 chrs 
   
' Typically, parameter values will be a variable set by your code or can be literal values/string.
' StyleString is only optional if it has been set in a Group that this Element is within.
' It is recommended to use variables or const for common position reference so groups of elements is easily set/changed

SVG Elements   XY position & Size are Long, Style is string, Rotate=Integer, StyleFormat=Ubyte.
   '                                                Default: Style="", Rotate=0, StyleFormat=1

   SVG_Line(10, 10, 200, 10, "StyleStr") 'X1, Y1, X2, Y2, StyleString, [Rotate], [StyleFormat]
   (X1 As Long, Y1 As Long, X2 As Long, Y2 As Long, ByVal StyleSVG As String="", RotateVal As Integer=0, StyleFmt As UByte=1) 
   
   SVG_Rect(250, 40, 200, 50, "StyleStr")
     '(X As Long, Y As Long, W As Long, H As Long, ByVal StyleSVG As String="", RotateVal As Integer=0, StyleFmt As UByte=1)

SVG_Circle(160, 200, 25, "StyleStr")
            '(Cx As Long, Cy As Long, R As Long, ByVal StyleSVG As String="", RotateVal As Integer=0, StyleFmt As UByte=1)

SVG_Ellipse(80, 180, 25, 15, "StyleStr")
  '(Cx As Long, Cy As Long, Rx As Long, Ry As long, ByVal StyleSVG As String="", RotateVal As Integer=0, StyleFmt As UByte=1)

SVG_Text(520, 75, "Text", "StyleStr")
       (X As Long, Y As Long, SVGstring As String, ByVal StyleSVG As String="", RotateVal As Integer=0, StyleFmt As UByte=1)
Example text style:  "font-family=verdana, font-size=24px, Font-Weight=bold, Fill=navy, stroke-width=1, stroke=black"

SVG_Qbezier(520, 150, 570, 100, 620, 150,  "StyleStr")
             '(X1 As Long, Y1 As Long, QX As Long, QY As Long, X2 As Long, Y2 As Long,  [ByVal StyleSVG As String=""], RotateVal As Integer=0, StyleFmt As UByte=1)

' Multtple Qbezier
QbezStr = "M 500,100 Q 550,50 600,100" +CR_LF +"M 600,100 Q 650,50 700,100" ' CR_LF is optional, improves SVG code structure.
SVG_Path("QbezStr", "StyleStr") '(QbezStr As String, [ByVal StyleSVG As String=""], RotateVal As Integer=0, StyleFmt As UByte=1)
          
             
PolyPoints = "350,100 379,181 469,161 397,215 423,301 350,250 277,301 303,215 231,161 321,181" ' first set the points 
SVG_PolyLine(PolyPoints, StyleStr,,)
             '(SVGpoints As String, ByVal StyleSVG As String="", RotateVal As Integer=0, StyleFmt As UByte=1)

PolyPoints = "350,100 379,181 469,161 397,215 423,301 350,250 277,301 303,215 231,161 321,181" ' first set the points
SVG_Polygon(PolyPoints, StyleStr,,)
             '(SVGpoints As String, ByVal StyleSVG As String="", RotateVal As Integer=0, StyleFmt As UByte=1)

   'SVG_Path  Path are somewhat complex (also first point of current part is last point of previous)
   
   SVGPathStr = "M 50,300 L 100,300 120,325 150,325 Q 175,300 200,325 225,350 250,325" ' on 1 line
   '            separate structured path string (easier to debug SVG) 
   SVGPathStr = "M 50,300" +CR_LF _                   ' Move to point x,y (first line start point)
              + "L 100,300 120,325 150,325" +CR_LF _  ' 3x Lines   (absolute points)  x,y x,y x,y
              + "Q 175,300 200,325 225,350 250,325"   ' 2x Quad Bezier curve  (absolute points)
     
   SVG_Path(SVGPathStr, "Fill=none, Stroke=black, Stroke-Width=2")          
          '(ByVal SVGPathStr As String, ByVal StyleSVG As String="", RotateVal As Integer=0, StyleFmt As UByte=1)
          
'   Upper/Lower case has different meaning in Path command: M m, L l, C c, Q q,        
'   An Ucase letter specifies absolute coordinates and a Lcase letter specifies relative coordinates 
   
'       Path for 2 x Cubic Bezier curve ( more complex) 
   SVGPathStr = " M 213,7" +" c -32 -14 -74,0 -88,30" +" C 110,5,67 -9,36,6" +" z"
   SVGPathStr = " M 213,7" +CR_LF _                ' Move to point x,y            
               +" c -32 -14 -74,0 -88,30" +CR_LF _ ' Cubic Bezier (c = relative points)
               +" C 110,5,67 -9,36,6" +CR_LF _     ' Cubic Bezier curve (C = absolute points)
               +" z"                               ' Line to start point 
        
'       Path for 4 x Quadratic Bezier curve
'       This example string has been generated by math code (see SVG_plot_CirclePoints.bas) ' CRLF ' is 2 chrs (just to "see" newline)  
     "M 650,201 Q 649,304 750,203 CRLF M 649,208 Q 634,310 748,223 CRLF M 648,215 Q 618,314 743,244 CRLF M 645,221 Q 602,315 736,263 CRLF"
'     Equivalent in literal form. (all 1 string)   
   SVGPathStr = "M 650,201 Q 649,304 750,203" &CR_LF _
               &"M 649,208 Q 634,310 748,223" &CR_LF _
               &"M 648,215 Q 618,314 743,244" &CR_LF _
               &"M 645,221 Q 602,315 736,263" &CR_LF"
   
'  Local image in SVG
   FilePath = "d:\Data\FBedit\DataGraph\" ' Optional (if file in same folder)
   FileName = "mdn_logo_only_color.png"
   SVG_Image(600, 300, 150, 150, , FilePath +FileName, -10)

'  Online image in SVG
   FilePath = ""
   FileName = "https://i.postimg.cc/qqT64fy9/mdn-logo-only-color.png"
   SVG_Image(600, 300, 150, 150, , FilePath +FileName, -10)
   '         (x, y, xSize, ySize, [KeepAspect], fileName, [Rotate])  Param 5 null = KeepAspect, "none" = stretch to XY Size

   ' Text on Path
   '  TextPath & Gradient fill both use a named def block (set unique name in first param,).
   '  First define the path 
   SVGPathStr = "M 700,325 Q 750,300 800,325 850,345 900,325"
   '  set path style, Text, text style & optional [start position%], [Show path] 
   SVG_TextPath("defd id", "SVGPathStr", "Path_Style", "Text_String", "Text_Style", Strt_Position %, ShowPath)
   '           (         ,              ,             ,              ,             ,  integer       , integer 1= show)                            
   ' Gradient Fill               Refer SVG gradient fill reference for details.
   SVG_Gradient("Grad1", "blue", "red", 0, 100, 0, 100, 0, 0)     ' (def_id, Color1, Coilor2, Stop1, Stop2, x1%,x2%,y1%,y2%)
   SVG_Ellipse(850, 400, 100, 50, "Stroke=1, fill=url(#Grad1)",,2)
   
   '  Param 4 & 5 set the mixing & position of 2 colors.
   '  For X axis gradient leave y1, y2 =0%, For Y axis gradient leave x1, x2 =0% 
   '  If x1,x2  are not equal the angle will be changed.  Same applies to y1,y2
   '  Keep case same in def & element eg. Grad1 & grad1 will not work.    

   
   ' Drop Shadow
   SVG_DropShadow("DSf1", "grey") ' or
   SVG_DropShadow("DSf1", "grey", 5, 10, 2) '( ID , Col , [x=10], [y=10], [feOffset=-20], [fesize=-180])     
   '             ( def ID, shadow color, X position, Y position, Blur intensity, Filter offset, Filter size)
   ' Numeric parameters all have default values. Param 6 & 7 only need to set if shadow gets clipped with large values.
   ' feDropShadow is applied in the defined filter region.(invisible) The last 2 parameters control the filter position & size.
   ' Both feOffset & feSize are relative to Element top left BBox. feOffset moves the filter. 
   '   eg. -20 = -20% diagonally left/up of Element size.  
   ' The following Element has the drop shadow applied by way of the filter attribute with ID DSf1.    
   SVG_PolyLine("700,230 750,230 770,210 728,210 700,230", "filter=url(#DSf1), stroke=brown, stroke-width=1, fill=navy, fill-opacity=0.75")
   ' You can use the same defined filter shadow on many elements.
   ' If you need a different shadow for another Element, make another SVG_DropShadow & a different ID name like DSf2 

                                                                                                            
   ' Gaussian Blur filter
   SVG_GaussianBlur("GBf1") ' or
   SVG_GaussianBlur("GBf1", 5, 5, 1.5, 0, 0) '( ID, [x=10], [y=10], Blur, [feOffset=-20], [fesize=-180])     
   '             ( def ID, shadow color, X position, Y position, Blur intensity, Filter offset, Filter size)   
Sub SVG_GaussianBlur(DS_ID As String, DX As Integer=0, DY As Integer=0, Blur As Single=2, feOffset As Integer= -20, feSize As Integer=150)
  
'------------------------------------

   SVG_CRLF    ' add line space to output 
   SVG_CRLF(n) ' [n] n = num of newline default=1
   
   SVG_Comment("String", 1) ' Comment String, [num of CRLF] default=0     
   ' Note: do not use "--" in your comment string, likely to cause error !
   
   
   SVG_Raw("String")   ' This allows raw SVG code to be placed in output, use to extend limitation of lib
                       ' or test a bug or new feature to improve lib.   
   
' Transformations:  alter the position, size, rotation, skew (or matrix transform) of Elements
'                   Only the type is specified eg. Rotate=10, Scale=0.5, SkewX=5, SkewY=5, Translate=(100,50)    
'                   (Matrix is untested for ezySVG lib, Test if you can & report)
' Transformations evaluate right to left in SVG. eazySVG lib structures Transform attributes for this order,  
' but be mindful if you encounter some anomaly or writing raw SVG & inspect the SVG line.
 



'  Utilities
'---------------------
'  GetPolyMid: find the centre & XY Min/Max (bounding box) for PolyPoints
'              Use with SVG_PolyLine & SVG_PolyGon rotate (default point is midXY)  
   SVG_GetPolyMid(PolyPoints, PolyMid()) 
   ' PolyPoints = String containing your points,  " x,y x,y x,y ... " 
   ' PolyMid()= Return value:     PolyMid(1)  Xmid
   '                            PolyMid(2)  Ymid
   '                            PolyMid(3)  Xmin
   '                            PolyMid(4)  Xmax
   '                            PolyMid(5)  Ymin
   '                            PolyMid(6)  Ymax


'   Notes: StyleStr, PolyPoints, PolyMid SVGPathStr above are your Variable names. or literal values.
'          Style attributes have default value, but some result in no Element rendered(Not visible).
           

' StyleString can also have an optional left part, this is the Transform Attributes separated by "__"(2x Underscore) from the Style (right part). 
' For some elements the additional attributes are included along with the Transform attributes, eg. rx & ry are round corners for rectangle. 
' Example: "rx=5, ry=5, rotate=5, scale=1.5, SkewX=5__Stroke=yellow, Stroke-Width=1"

' Rotate can be applied in 2 ways. 
'        1. A non zero value in the second last parameter.
'        2. Transform attributes in the left part of Style string (can set rotate origin point)
' Rotate(in style string) has 2 modes: Rotate=12 uses lib default rotate point, Rotate=(12,100,220) uses the points set.
' So you can choose the rotate point for elements. (it may take some experimenting sometimes)
' For easy rotate there is also the rotate parameter. use either but not both.
' SVG by default rotates from the 0,0 origin, this lib defaults to Element X,Y position or centre.

' Color style attributes:  The following will set the color yellow in a  'fill=  '   or   'stroke=  '.
	RGB(255,255,0)     'RGB in decimal values
	RGB(100%,100%,0%)  'RGB in percent
	#ffff00            'Hex rrggbb  (must have "#" prefix)
	yellow             '(refer CSS color names)
'	16776960       <-- But NOT pure decimal value, it will default rgb(00,00,00) or may give error.

'Notes:
'      For an unfilled Element fill must be specified Fill=none or Fill=transparent.
'      "&" is an illegal chr, replace with &#38;
'      For closer SVG syntax check, compile without html & drop svg file onto browser (Tested with firefox), this
'      will not find simple case errors though.   

VectorGraph_Example.bas

Code: Select all

 
' Test program for ezySVG.bi lib
' This code needs some work, Graph offset reference need improving.
' Window9.bi lib commands left in place (SVG code shadowed these commands for easy SVG printout) 
' from my main CSV DataGraph program. 

#include "ezySVG.bi"

Declare Sub DrawGrid 
Declare Sub DrawTicks

 Dim As String  TextStr, StyleStr
 Dim Shared As Integer ViewSizeX, ViewSizeY, Xmid, Ymid
 Dim Shared As Uinteger FillCol, LineCol, LineW, X, Y, Lp1
 Dim As String SVGstyle, HexRGB1, HexRGB2 
 Dim Shared As Integer Graf1PosX, Graf1PosY, Graf1SizeX, Graf1SizeY, Graf1EndPosX, _ 
                       Graf1EndPosY, TickLenX, TickLenY, GridX, GridY, MarginY2, Graf1MidX, Graf1MidY 

'  Open "debug_Ref00.txt" For Output As #5

   ViewSizeX = 1000
   ViewSizeY = 500
   Xmid = ViewSizeX/2
   Ymid = ViewSizeY/2 

   GridX = 30 ' num of grid lines
   GridY = 20
   Graf1PosX  = 100 ' distance to ViewBox edge (both sides)
   Graf1PosY  = 50
   MarginY2 = 50    '< ! this shifts 0,0 origin of graph midpoint (need to fix so GraphMidY stays at 0,0)
   TickLenX = 10
   TickLenY = 10 ' Graph Ticks
   Graf1SizeX = ViewSizeX - (Graf1PosX * 2) 
   Graf1SizeY = ViewSizeY - (Graf1PosY  + MarginY2)  
   Graf1PosX = -Xmid +Graf1PosX
   Graf1PosY = -Ymid +Graf1PosY
   Graf1EndPosX = Graf1PosX + Graf1SizeX : Graf1EndPosY = Graf1PosY + Graf1SizeY
   Graf1MidX = Graf1EndPosX - (Graf1EndPosX-Graf1PosX)/2   
   Graf1MidY = Graf1EndPosY- (Graf1EndPosY-Graf1PosY)/2
      
'      Print #5,    "Graf1PosY = " +Str(Graf1PosY) 'DEBUG
'      Print #5, "Graf1EndPosY = " +Str(Graf1EndPosY) 'DEBUG
      
      
      SVG_Start("Test00Ref.SVG", ViewSizeX, ViewSizeY, -Xmid, -Ymid, "html") ' with offset
'      SVG_Start("Test00Ref.SVG", ViewSizeX, ViewSizeY, 0, 0, "html")

'      FormatStyle("fill=silver, stroke=black, stroke-width=2", 1)    'TESTingONLY Private function
   
'      SVG_Rect(-20, -20, 20, 20, "rx=5, ry=5_fill=silver, stroke=black, stroke-width=2")   ' test offset
  	   ' Rectangle to fit SVG "Canvas"
  	   StyleStr = "fill=silver, stroke=black, stroke-width=2"', fill-opacity=1, stroke-opacity=1"
  	   SVG_Comment("   __This rect fills the ViewBox  __", 1)
  	   SVG_Rect(-500, -250, ViewsizeX, ViewSizeY, StyleStr) ' fill viewBox BG
      TextStr = " DataGraph Example "
      SVG_Text(Graf1MidX, Graf1PosY-5, TextStr, "font-family=verdana, font-size=18px, Font-Weight=bold, Fill=navy, text-anchor=middle",,  1)
      
  	    SVG_CRLF  '<- put a newline in SVG code to improve structure (no effect on graphic)
  	   SVG_Line(-Xmid, 0, Xmid, 0, "stroke=rgb(130 130 130), stroke-width=1, stroke-dasharray=(8,12)")
  	   SVG_Line(0, -Ymid, 0, Ymid, "stroke=rgb(130,130,130), stroke-width=1, stroke-dasharray=(8 12)")

  	   SVGStyle = "Rx=4, Ry=4_fill=#e9faff, stroke=black, stroke-width=2" ' Note the radius attributes
  	   SVG_Rect(Graf1PosX, Graf1PosY, Graf1SizeX, Graf1SizeY, SVGStyle) 'GraphBox

  	    SVG_CRLF
  	   SVG_Path("M 0,-20 L 0, 20 M -20,0 L 20,0", "Fill=none, Stroke=rgb(0,0,0),stroke-width=1") ' crosshairs
  	   SVG_Text(1,10, "0,0", "Font-style=italic ,font-size=12px")
  	   ' Text at ViewBox X centre
      DrawGrid
      DrawTicks
  	
  	   SVG_Line(Graf1PosX, Graf1MidY, Graf1EndPosX, Graf1MidY, "stroke=rgb(170,170,220), stroke-width=1")
  	   SVG_Line(Graf1MidX, Graf1PosY, Graf1MidX, Graf1EndPosY, "stroke=rgb(170,170,220), stroke-width=1")

   SVG_CRLF
   SVG_end("html")
   Beep  ' I am done
   
' Close #5  '< DEBUG

End
   
  'Sub DrawGrid(Graf1SizeX, Graf1SizeY, Graf1PosX, Graf1PosY, Graf1EndPosX, Graf1EndPosY, GridX, GridY)
   Sub DrawGrid
	   Dim As Integer Lp1
	   Dim As Single GridW
	   Dim As String SVGStyle
	   SVG_CFLF : SVG_Comment(" __Graph Grid (Lines)__ ",1)
	   SVGStyle = "stroke=rgb(130,130,130), stroke-width=0.25, stroke-dasharray=(4,4)"   
	   SVG_Group(SVGStyle)     ' set SVG group start & group styles
        'X
	For Lp1 = 1 To GridX-1	      ' num of grid lines
	   GridW = Graf1SizeX/GridX   ' Grid spacing in pixels
'	   LineDraw(Graf1PosX+GridW*Lp1, Graf1PosY, Graf1PosX+GridW*Lp1, Graf1EndPosY, 1, darkgrey,PS_DOT)
	   SVG_Line(Graf1PosX+GridW*Lp1, Graf1PosY, Graf1PosX+GridW*Lp1, Graf1EndPosY,,,0) 
	Next
        'Y
	For Lp1 = 1 To GridY-1
	   GridW = Graf1SizeY/GridY              'Ref:  LineDraw(x1,y1,x2,y2,Width,Color,style)
'	   LineDraw(Graf1PosX,Graf1PosY+GridW*Lp1, Graf1EndPosX, Graf1PosY+GridW*Lp1, 1, darkgrey,PS_DOT)
	   SVG_Line(Graf1PosX,Graf1PosY+GridW*Lp1, Graf1EndPosX, Graf1PosY+GridW*Lp1,,,0) 'SVG
	Next
	SVG_Group("End")             ' End SVG Group
End Sub  


Sub DrawTicks
    Dim As UInteger Lp1
    Dim As Single GridW
    Dim As String SVGStyle
	   SVG_Comment(" __Graph Ticks (Lines)__ ")
      SVGStyle = "   stroke=rgb(0,0,0), stroke-width=0.5"
	   SVG_Group(SVGStyle)
      SVG_Comment("    X")
   'X (Lower)
	For Lp1 = 0 To GridX	         ' num of grid lines
	   GridW = Graf1SizeX/GridX   ' Grid width in pixels
'	   LineDraw(Graf1PosX+(GridW*Lp1), Graf1EndPosY, Graf1PosX+(GridW*Lp1), Graf1EndPosY+TickLenX, 1, black,PS_SOLID)
	   SVG_Line(Graf1PosX+(GridW*Lp1), Graf1EndPosY, Graf1PosX+(GridW*Lp1), Graf1EndPosY+TickLenX,,,0) 'SVG  
	Next
      SVG_Comment("    Y")
   'y1 (L&R)
	For Lp1 = 0 To GridY
	   GridW = Graf1SizeY/GridY
'	   LineDraw(Graf1PosX+3, Graf1PosY+(GridW*Lp1), Graf1PosX-TickLenY, Graf1PosY+(GridW*Lp1), 1, black,PS_SOLID)
	   SVG_Line(Graf1PosX+3, Graf1PosY+(GridW*Lp1), Graf1PosX-TickLenY, Graf1PosY+(GridW*Lp1),,,0) 'SVG
'	   LineDraw(Graf1EndPosX, Graf1PosY+(GridW*Lp1), Graf1EndPosX+TickLenY, Graf1PosY+(GridW*Lp1),1, black,PS_SOLID)
	   SVG_Line(Graf1EndPosX, Graf1PosY+(GridW*Lp1), Graf1EndPosX+TickLenY, Graf1PosY+(GridW*Lp1),,,0) 'SVG  
	Next
      SVG_Group()        ' End SVG Group

'Print #5, " Style Back "; SVGStyle
End Sub

   


Some images of SVG output printed > pdf > png:

Test ezySVG_1.bas output
Image

DataGraph output - Simulation
Image

DataGraph output - Real data
Image

VectorGraph_Example
https://i.postimg.cc/VvJ5Ft7C/Vector-Gr ... le-SVG.png

TestSVG_1.svg.html output
Open in browser & view source (SVG code should be syntax colors)

Code: Select all

<!DOCTYPE html>
<html>
<body>

<svg width="1000" height="500" viewbox="0,0,1000,500"
>

<!--    This rect fills the ViewBox -->
<rect x="0" y="0" width="1000" height="500" style="fill:Silver;stroke:black;stroke-width:2"/>

<line x1="500" y1="0" x2="500" y2="500" style="stroke:rgb(130 130 130);stroke-width:1;stroke-dasharray:8 12"/>
<line x1="0" y1="250" x2="1000" y2="250" style="stroke:rgb(130,130,130);stroke-width:1;stroke-dasharray:8 12"/>

<text x="500" y="25"  style="font-family:verdana;font-size:18px;font-weight:bold;fill:navy;text-anchor:middle">ezySVG Test  2024.02.11</text>
<text x="25" y="250" transform="rotate(-90,25,250)"  style="text-anchor:middle">Rotate Left (middle anchor)</text>
<text x="975" y="250" transform="rotate(90,975,250)"  style="font-family:verdana;fill:purple;text-anchor:middle">Rotated Right (middle anchor)</text>
<text x="520" y="75"  style="font-family:verdana;font-size:24px;font-weight:bold;fill:none;stroke-width:1;stroke:black">Text - Outline only</text>
<text x="10" y="490"  style="font-style:italic;font-size:12px">If viewing this graphic in a web browser, view page source to see SVG code(syntax color'd)</text>
<text x="1" y="10"  style="font-style:italic;font-size:12px">0,0</text>
<text x="501" y="260"  style="font-style:italic;font-size:12px">500,250</text>

<!--  some lines... -->
<line x1="10" y1="40" x2="200" y2="40" style="stroke:green;stroke-width:2"/>
<line x1="600" y1="40" x2="700" y2="40" transform="rotate(5,600,40)" style="stroke:green;stroke-width:2"/>

<!--    This is a style group -->
<g
   stroke="blue"
   stroke-width="2"
   stroke-dasharray="8 2"
>
   <line x1="10" y1="60" x2="200" y2="60"/>
   <line x1="10" y1="70" x2="200" y2="70"/>
   <line x1="10" y1="80" x2="200" y2="80" stroke="Purple" stroke-width="3" stroke-dasharray="0"/>
   <line x1="10" y1="90" x2="200" y2="90"/>
   <line x1="10" y1="100" x2="200" y2="100"/>
   <line x1="10" y1="110" x2="200" y2="110" transform="rotate(5,10,110) translate(10,110) scale(1.5) translate(-10,-110)" stroke="yellow" stroke-width="1" stroke-dasharray="3 5"/>
</g>

<text x="10" y="125" transform="rotate(5,10,125)"  style="font-size:12px">6 lines above are in a style group,  2 lines have style override</text>
<line x1="230" y1="40" x2="475" y2="90" style="stroke:red;stroke-width:5"/>
<rect x="250" y="40" width="200" height="50" style="fill:#C0E2D5;stroke:black;stroke-width:2;fill-opacity:0.6"/>
<ellipse cx="80" cy="180" rx="25" ry="15" style="fill:#E2C0D5;stroke:RGB(00,175,255);stroke-width:3"/>
<ellipse cx="80" cy="220" rx="25" ry="15" transform="rotate(45,80,220)" style="fill:#E2C0D5;stroke:RGB(00,75,255);stroke-width:2"/>
<circle cx="160" cy="200" r="25" style="fill:#E2C0D5;stroke:RGB(00,00,255);stroke-width:3"/>
<circle cx="200" cy="200" r="30" style="fill:none;stroke:black;stroke-width:2;stroke-opacity:0.5"/>

<rect x="950" y="45" width="30" height="8" style="stroke:#005DC0;fill:#0BA9B9;stroke-width:1"/>
<line x1="45" y1="371" x2="500" y2="371" style="stroke-width:1;stroke-opacity:0.6;stroke:red"/>
<line x1="45" y1="372" x2="500" y2="372" style="stroke-width:1;stroke-opacity:0.6;stroke:green"/>
<line x1="45" y1="373" x2="500" y2="373" style="stroke-width:1;stroke-opacity:0.6;stroke:blue"/>
<text x="950" y="30"  style="font-size:3px"> Some tiny text to see</text>
<text x="950" y="35"  style="font-size:3px"> Zoom in to read</text>
<text x="950" y="40"  style="font-size:3px"> SVG Doesnt do MultiLine Text!</text>
<polygon points= "350,100 379,181 469,161 397,215 423,301 350,250 277,301 303,215 231,161 321,181"
 style="fill:none;stroke:red;stroke-width:1;stroke-dasharray:2 3"/>
<polygon points= "350,100 379,181 469,161 397,215 423,301 350,250 277,301 303,215 231,161 321,181"
 transform="rotate(20,350,200)" style="fill:none;stroke:blue;stroke-width:1;stroke-dasharray:4 8"/>
<polygon points= "350,100 379,181 469,161 397,215 423,301 350,250 277,301 303,215 231,161 321,181"
 transform="rotate(-12,350,200)" style="fill:none;stroke:green;stroke-width:1;stroke-dasharray:4 4"/>
<text x="350" y="340"  style="font-size:13px">PolyGon (rotated)</text>

<!--    Poly Line -->
<polyline points= "300,400 350,450 400,400 300,400"
 transform="rotate(2,350,450)" style="stroke:blue;stroke-width:2"/>
<polyline points= "300,400 350,450 400,400 300,400"
 transform="rotate(90,350,425)" style="stroke:yellow;stroke-width:1;fill:none"/>
<text x="400" y="465"  style="font-size:13px">PolyLine</text>
<rect x="330" y="200" width="40" height="40" transform="rotate(45,350,220)" style="fill:#C0E2D5;stroke:black;stroke-width:2;fill-opacity:0.6"/>
<circle cx="350" cy="200" r="5" style="fill:none;stroke:black;stroke-width:1;stroke-opacity:0.9"/>

<!--    Quad Bezier curve -->
<path  d="M520 150 Q 560 30, 620 150" transform="rotate(15,570,150)" style="stroke:black;stroke-width:2;fill:purple;fill-opacity:0.4"/>
<path  d="M520 150 Q 570 150, 620 150" transform="rotate(15,570,150)" style="stroke:white;stroke-width:3;fill:none"/>
<text x="520" y="190"  style="font-size:13px">Quad Bezier curve (1 control point)</text>
<text x="520" y="205"  style="font-size:13px">Rotated 25deg. with fill &#38; Baseline</text>

<rect x="700" y="100" width="80" height="40" rx="8" ry="8" style="fill:darkblue;stroke:green;stroke-width:3"/>
<rect x="800" y="100" width="80" height="40" rx="8" ry="8" transform="skewX(10)" style="fill:none;stroke:black;stroke-width:1"/>

<!-- SVG Paths -->
<path d="M 213,7 c -32 -14 -74,0 -88,30  C 110,5,67 -9,36,6  z" style="fill:none;stroke:black"/>
<path d="M 213,7 c -32 -14 -74,0 -88,30 C 110,5,67 -9.5,36,6" transform="translate(0,440) rotate(15,213,7)" style="fill:none;stroke:blue"/>
<text x="50" y="465"  style="font-size:13px">Path(2x C.Bezier curve)</text>
<path d="M 50,300 L 100,300 120,325 150,325 Q 175,300 200,325 225,350 250,325" style="fill:none;stroke:black"/>
<text x="50" y="355"  style="font-size:13px">Path(3x lines, 2x Qbezier curve</text>
<path d="M 500,220 L 500, 280 M 470,250 L 530,250" style="fill:none;stroke:black"/>
<path d="M 100,400 Q 175,375 200,400 175,425 100,400" style="fill:none;stroke:blue"/>


<!--     Raster Image in SVG -->
<rect x="550" y="280" width="150" height="200" style="fill:none;stroke:grey;stroke-dasharray:1 3"/>
<image href="https://i.postimg.cc/qqT64fy9/mdn-logo-only-color.png" x="550" y="280" width="150" height="200" preserveAspectRatio="xMidyMid"/>
<text x="550" y="475"  style="font-size:13px">Raster image</text>

<!--     Text on a Path -->
<defs>
  <path id="Path1" d="M 700,325 Q 750,300 800,325 850,345 900,325"  style="fill:none;stroke:blue;stroke-width:1;stroke-dasharray:2 2"></path>
</defs>
  <use href="#Path1"></use>
<text  style="font-size:17px">
  <textPath  href="#Path1" startOffset="8%">
    Text Along a Path - Cool
  </textPath>
</text>


<!--     Drop Shadow effect -->
 <defs>
   <filter id="DSf1" x="-20%" y="-20%" width="180%" height="180%">
      <feDropShadow dx="5" dy="10" stdDeviation="2" flood-color="grey" />
   </filter>
 </defs>

<polyline points= "700,230 750,230 770,210 728,210 700,230"
 style="filter:url(#DSf1);stroke:brown;stroke-width:1;fill:navy;fill-opacity:0.75"/>

<!--     GaussianBlur effect -->
 <defs>
   <filter id="GB1" x="-20%" y="-20%" width="180%" height="180%">
      <feGaussianBlur stdDeviation="0.35" result="GB1" />
      <feOffset dx="2" dy="2" />
   </filter>
 </defs>

<g
   text-anchor=" middle"
   font-size="24px font-weight=bold"
   font-family="verdana"
>
   <text x="820" y="270"    filter="url(#GB1)"
   fill="#505050">TextShadow using GaussianBlur</text>
   <text x="820" y="270"    fill="navy">TextShadow using GaussianBlur</text>
</g>


<!--     Linear Gradient Fill -->
 <defs>
   <linearGradient id="Grad1" x1="0%" x2="100%" y1="0%" y2="0%">
      <stop offset="0%" stop-color="blue" />
      <stop offset="100%" stop-color="red" />
   </linearGradient>
 </defs>

 <defs>
   <filter id="DSf1" x="-20%" y="-20%" width="180%" height="180%">
      <feDropShadow dx="12" dy="12" stdDeviation="1.5" flood-color="grey" />
   </filter>
 </defs>

<ellipse cx="850" cy="400" rx="100" ry="50" fill="url(#Grad1)" filter="url(#DSf1)"/>
<circle cx="850" cy="400" r="30" fill="url(#Grad1)" stroke="black" stroke-width="2" stroke-opacity="0.5"/>
<text x="750" y="475"  style="font-size:13px">Gradient Fill &#38; Drop Shadow</text>
<text x="825" y="200"  style="font-size:13px">skewX Transform</text>

</svg>

</body>
</html>
(png) - postimg can't do pdf
https://i.postimg.cc/ZRh0T6g4/Test-SVG- ... Page-1.png

https://i.postimg.cc/rwKDgVxX/Test-SVG- ... Page-2.png

https://i.postimg.cc/DZd0m8ks/Test-SVG- ... Page-3.png
Last edited by Tonigau on Feb 17, 2024 8:57, edited 5 times in total.
srvaldez
Posts: 3379
Joined: Sep 25, 2005 21:54

Re: ezySVG Lib for Freebasic - make SVG files

Post by srvaldez »

very impressive!
thanks for sharing 👍😁
srvaldez
Posts: 3379
Joined: Sep 25, 2005 21:54

Re: ezySVG Lib for Freebasic - make SVG files

Post by srvaldez »

hello Tonigau :)
a couple of errors in ezySVG.bi
in functions SVG_Polygon and SVG_PolyLine the array PolyBB is an array of Long but in the function SVG_GetPolyMid it's an array of integer
in TestSVG_1.bas line 202 you have SVG_Image(550, 280, 150, 200, "xMidyMid", FilePath +FileName, 0) but FilePath is undefined, I changed it to PathString and all seem ok

I don't know how to test "TestingSVG Cmds.bas", as is there are too many errors of variables not declared
can't compile "VectorGraph_Example.bas" because of missing "FB_SVGout.bi"
Tonigau
Posts: 36
Joined: Feb 25, 2021 20:19

Re: ezySVG Lib for Freebasic - make SVG files

Post by Tonigau »

Thanks for finding some issues, much appreciated.
I am sure there is more stuff that could be done better as well & welcome any feedback.

The code in "TestingSVG Cmds.bas" is not meant to compile, I will rename to more like help.., I made it .bas to load in an FB IDE to see code better.

There are some changes I will upload tomorrow added:
feDropShadow filter,
convert "&" to "&#38;" for text strings ,
change Style string separator "_" to "__"
in TestSVG_1.bas line 202 you have SVG_Image(550, 280, 150, 200, "xMidyMid", FilePath +FileName, 0) but FilePath is undefined, I changed it to PathString and all seem ok
Fixed, there was an issue with selecting image resource path(online, Local/Local with full path)
PathString is an internal sub var for the SVG_Path (list of xy points) I will rename it to avoid confusion.

oops , I forgot to update include name in "VectorGraph_Example.bas" (should be #Include "ezySVG.bi")
Tonigau
Posts: 36
Joined: Feb 25, 2021 20:19

Re: ezySVG Lib for Freebasic - make SVG files

Post by Tonigau »

I am not that good with geometric vector math & patterns, if someone wants to show their artistic talent (& we might find some issues with the lib)
Best to use lower case for Style names & value (unless you know specific case sensitive SVG syntax)

cheers

--------------------------------
2024.02.17
I had a play with some math for points on circle...
Some very interesting patterns arose when I had errors in the code/formula, took a while to get it right...

SVG_plot_CirclePoints.bas

Code: Select all

' SVG_plot_CirclePoints
' Simple vector math example for ezySVG.bi & Quad Bezier curve using SVG_Path
' 

#Include "ezySVG.bi"

Dim as Integer Angle, CtrX, CtrY, Radius1, Radius2, Step_, bzOffset
Dim as Integer Circle_PtsX1, Circle_PtsY1, Circle_PtsX2, Circle_PtsY2, Circle_PtsXbz, Circle_PtsYbz, Cnt1 
Dim as Single Radian1, Radian2, PI = 3.14159
Dim As String StyleStr, QbezStr 
Dim As Long VboxSizeX = 1000, VboxSizeY = 500 

Const CR_LF = Chr(13,10)
Const    DQ = Chr(34)
Const html_ = "html"  
'Const html_ = ""    
'                                                                       [optional param SVGstr]
   SVG_Start("TestPlotCircle_1.SVG", VboxSizeX, VboxSizeY, 0, 0, html_, "xmlns=""http://www.w3.org/2000/svg""")

   StyleStr = "rx=20, ry=20__Fill=Silver, stroke=darkgreen, stroke-width=2"
       SVG_Comment("   This rect fills the ViewBox", 1)
       SVG_Rect(1, 1, VboxSizeX-2, VboxSizeY-2, StyleStr)
   
   Radius1 = 50  : Radius2 = 150      ' inner : outer
   CtrX    = 200 : CtrY    = 200      ' centre

   SVG_Comment("   Spoke object 1", 1)
   Step_ = 4                          ' 360/Step_ = nim of points
   For Lp1 As Integer = 1 To 360 Step Step_
      Radian1 = Lp1 * PI / 180    ' degree --> Radian (for cos/sin)
 
      Circle_PtsX2 = (CtrX + 30) + Radius2 * Cos(Radian1) 'plot the points
      Circle_PtsY2 =  CtrY + Radius2 * Sin(Radian1)       '  "       "
      SVG_Line(CtrX, CtrY, Circle_PtsX2, Circle_PtsY2,"stroke=navy, stroke-width=1")       ' spokes
      SVG_Circle(Circle_PtsX2, Circle_PtsY2, 2, "stroke=black, stroke-width=1, fill=pink") ' circle points
   Next
      SVG_Circle(CtrX, CtrY, 7, "stroke=#606060, stroke-width=1, fill=black")            ' infinite gravity
   
      Step_ = 8     
      CtrX += 400
      Cnt1 = 1
      bzOffset = 8
      QbezStr = ""
      SVG_Comment("   Spoke object 2", 1)
   For Lp1 As Integer = 1 To 360 Step Step_
      Radian1 = Lp1 * 3.14159 / 180
      Radian2 = (Lp1 +Step_ * bzOffset) * 3.14159 / 180   ' bezier handle 
 
      ' points on circle math
      Circle_PtsX1  = CtrX + Radius1 * Cos(Radian1)
      Circle_PtsY1  = CtrY + Radius1 * Sin(Radian1)
      Circle_PtsX2  = CtrX + Radius2 * Cos(Radian1)
      Circle_PtsY2  = CtrY + Radius2 * Sin(Radian1)
      Circle_PtsXbz = CtrX + (Radius2-Radius1+15) * Cos(Radian2)
      Circle_Ptsybz = CtrY + (Radius2-Radius1+15) * Sin(Radian2)

      SVG_Line(Circle_PtsX1, Circle_PtsY1, Circle_PtsX2, Circle_PtsY2,"stroke=#606060, stroke-width=1, stroke-dasharray=(2,4)")
      SVG_Rect(Circle_PtsX2-4, Circle_PtsY2-2, 8, 4, "stroke=black, stroke-width=1, fill=pink", Lp1+90) '<  on circle points
      QbezStr &= "M " +Str(Circle_PtsX1) +"," +Str(Circle_PtsY1) _ 
               +" Q " +Str(Circle_PtsXbz) +"," +Str(Circle_PtsYbz) +" " +Str(Circle_PtsX2) +"," +Str(Circle_PtsY2) + CR_LF
      SVG_Circle(Circle_PtsXbz, Circle_PtsYbz, 2, "stroke=black, stroke-width=1, fill=red")             '<  Bezier Point
   Next
   
      SVG_Comment("   Quad Bezier in Path", 1)
      SVG_Path(QbezStr, "stroke=navy, stroke-width=1, fill=none")
      SVG_CRLF()
      SVG_Circle(CtrX, CtrY, 50, "stroke=navy, stroke-width=2, fill=none")
      SVG_Text(430, 25,  "Points On a Circle ", "font-family=verdana, font-size=18px, Font-Weight=bold, fill=navy, text-anchor=middle",,1)
      SVG_Text(200, 400, "Line spokes ", "font-family=verdana, font-size=12px, Font-Weight=bold, fill=navy, text-anchor=middle",,1)
      SVG_Text(600, 400, "Quad Bezier curves using Path ", "font-family=verdana, font-size=12px, Font-Weight=bold, fill=navy, text-anchor=middle",,1)
      SVG_Text(600, 420, "Red dot is the Bezier handle(from another spoke) ", "font-family=verdana, font-size=10px, Font-Weight=bold, fill=navy, text-anchor=middle",,1)
   
   SVG_CRLF
   SVG_End(html_)
   Beep 
'   Sleep

' Note:  For bezier curve in path - Maybe better to put Circle_Pts in an array first, then build Path points (QbezStr)
'        using loop & comditional statements
'        Adding optional CRLF's to the String "QbezStr" improves SVG code & enable indenting.
'        This is just 1 example of Qbezier path  string structure
Output code: TestPlotCircle_1.SVG.html

Code: Select all

<!DOCTYPE html>
<html>
<body>

<svg width="1000" height="500" viewbox="0,0,1000,500" xmlns="http://www.w3.org/2000/svg">

<!--    This rect fills the ViewBox -->
<rect x="1" y="1" width="998" height="498" rx="20" ry="20" style="Fill:Silver;stroke:darkgreen;stroke-width:2"/>

<!--    Spoke object 1 -->
<line x1="200" y1="200" x2="380" y2="203" style="stroke:navy;stroke-width:1"/>
<circle cx="380" cy="203" r="2" style="stroke:black;stroke-width:1;fill:pink"/>
<line x1="200" y1="200" x2="379" y2="213" style="stroke:navy;stroke-width:1"/>
<circle cx="379" cy="213" r="2" style="stroke:black;stroke-width:1;fill:pink"/>
<line x1="200" y1="200" x2="378" y2="223" style="stroke:navy;stroke-width:1"/>
<circle cx="378" cy="223" r="2" style="stroke:black;stroke-width:1;fill:pink"/>
<line x1="200" y1="200" x2="376" y2="234" style="stroke:navy;stroke-width:1"/>
<circle cx="376" cy="234" r="2" style="stroke:black;stroke-width:1;fill:pink"/>
<line x1="200" y1="200" x2="373" y2="244" style="stroke:navy;stroke-width:1"/>
<circle cx="373" cy="244" r="2" style="stroke:black;stroke-width:1;fill:pink"/>
<line x1="200" y1="200" x2="370" y2="254" style="stroke:navy;stroke-width:1"/>
<circle cx="370" cy="254" r="2" style="stroke:black;stroke-width:1;fill:pink"/>
<line x1="200" y1="200" x2="366" y2="263" style="stroke:navy;stroke-width:1"/>
<circle cx="366" cy="263" r="2" style="stroke:black;stroke-width:1;fill:pink"/>
<line x1="200" y1="200" x2="361" y2="273" style="stroke:navy;stroke-width:1"/>
<circle cx="361" cy="273" r="2" style="stroke:black;stroke-width:1;fill:pink"/>
<line x1="200" y1="200" x2="356" y2="282" style="stroke:navy;stroke-width:1"/>
<circle cx="356" cy="282" r="2" style="stroke:black;stroke-width:1;fill:pink"/>
<line x1="200" y1="200" x2="350" y2="290" style="stroke:navy;stroke-width:1"/>
<circle cx="350" cy="290" r="2" style="stroke:black;stroke-width:1;fill:pink"/>
<line x1="200" y1="200" x2="343" y2="298" style="stroke:navy;stroke-width:1"/>
<circle cx="343" cy="298" r="2" style="stroke:black;stroke-width:1;fill:pink"/>
<line x1="200" y1="200" x2="336" y2="306" style="stroke:navy;stroke-width:1"/>
<circle cx="336" cy="306" r="2" style="stroke:black;stroke-width:1;fill:pink"/>
<line x1="200" y1="200" x2="328" y2="313" style="stroke:navy;stroke-width:1"/>
<circle cx="328" cy="313" r="2" style="stroke:black;stroke-width:1;fill:pink"/>
<line x1="200" y1="200" x2="320" y2="320" style="stroke:navy;stroke-width:1"/>
<circle cx="320" cy="320" r="2" style="stroke:black;stroke-width:1;fill:pink"/>
<line x1="200" y1="200" x2="312" y2="326" style="stroke:navy;stroke-width:1"/>
<circle cx="312" cy="326" r="2" style="stroke:black;stroke-width:1;fill:pink"/>
<line x1="200" y1="200" x2="303" y2="331" style="stroke:navy;stroke-width:1"/>
<circle cx="303" cy="331" r="2" style="stroke:black;stroke-width:1;fill:pink"/>
<line x1="200" y1="200" x2="293" y2="336" style="stroke:navy;stroke-width:1"/>
<circle cx="293" cy="336" r="2" style="stroke:black;stroke-width:1;fill:pink"/>
<line x1="200" y1="200" x2="284" y2="340" style="stroke:navy;stroke-width:1"/>
<circle cx="284" cy="340" r="2" style="stroke:black;stroke-width:1;fill:pink"/>
<line x1="200" y1="200" x2="274" y2="343" style="stroke:navy;stroke-width:1"/>
<circle cx="274" cy="343" r="2" style="stroke:black;stroke-width:1;fill:pink"/>
<line x1="200" y1="200" x2="264" y2="346" style="stroke:navy;stroke-width:1"/>
<circle cx="264" cy="346" r="2" style="stroke:black;stroke-width:1;fill:pink"/>
<line x1="200" y1="200" x2="253" y2="348" style="stroke:navy;stroke-width:1"/>
<circle cx="253" cy="348" r="2" style="stroke:black;stroke-width:1;fill:pink"/>
<line x1="200" y1="200" x2="243" y2="349" style="stroke:navy;stroke-width:1"/>
<circle cx="243" cy="349" r="2" style="stroke:black;stroke-width:1;fill:pink"/>
<line x1="200" y1="200" x2="233" y2="350" style="stroke:navy;stroke-width:1"/>
<circle cx="233" cy="350" r="2" style="stroke:black;stroke-width:1;fill:pink"/>
<line x1="200" y1="200" x2="222" y2="350" style="stroke:navy;stroke-width:1"/>
<circle cx="222" cy="350" r="2" style="stroke:black;stroke-width:1;fill:pink"/>
<line x1="200" y1="200" x2="212" y2="349" style="stroke:navy;stroke-width:1"/>
<circle cx="212" cy="349" r="2" style="stroke:black;stroke-width:1;fill:pink"/>
<line x1="200" y1="200" x2="201" y2="347" style="stroke:navy;stroke-width:1"/>
<circle cx="201" cy="347" r="2" style="stroke:black;stroke-width:1;fill:pink"/>
<line x1="200" y1="200" x2="191" y2="345" style="stroke:navy;stroke-width:1"/>
<circle cx="191" cy="345" r="2" style="stroke:black;stroke-width:1;fill:pink"/>
<line x1="200" y1="200" x2="181" y2="342" style="stroke:navy;stroke-width:1"/>
<circle cx="181" cy="342" r="2" style="stroke:black;stroke-width:1;fill:pink"/>
<line x1="200" y1="200" x2="171" y2="338" style="stroke:navy;stroke-width:1"/>
<circle cx="171" cy="338" r="2" style="stroke:black;stroke-width:1;fill:pink"/>
<line x1="200" y1="200" x2="162" y2="334" style="stroke:navy;stroke-width:1"/>
<circle cx="162" cy="334" r="2" style="stroke:black;stroke-width:1;fill:pink"/>
<line x1="200" y1="200" x2="153" y2="329" style="stroke:navy;stroke-width:1"/>
<circle cx="153" cy="329" r="2" style="stroke:black;stroke-width:1;fill:pink"/>
<line x1="200" y1="200" x2="144" y2="323" style="stroke:navy;stroke-width:1"/>
<circle cx="144" cy="323" r="2" style="stroke:black;stroke-width:1;fill:pink"/>
<line x1="200" y1="200" x2="136" y2="317" style="stroke:navy;stroke-width:1"/>
<circle cx="136" cy="317" r="2" style="stroke:black;stroke-width:1;fill:pink"/>
<line x1="200" y1="200" x2="128" y2="310" style="stroke:navy;stroke-width:1"/>
<circle cx="128" cy="310" r="2" style="stroke:black;stroke-width:1;fill:pink"/>
<line x1="200" y1="200" x2="120" y2="302" style="stroke:navy;stroke-width:1"/>
<circle cx="120" cy="302" r="2" style="stroke:black;stroke-width:1;fill:pink"/>
<line x1="200" y1="200" x2="113" y2="294" style="stroke:navy;stroke-width:1"/>
<circle cx="113" cy="294" r="2" style="stroke:black;stroke-width:1;fill:pink"/>
<line x1="200" y1="200" x2="107" y2="286" style="stroke:navy;stroke-width:1"/>
<circle cx="107" cy="286" r="2" style="stroke:black;stroke-width:1;fill:pink"/>
<line x1="200" y1="200" x2="101" y2="277" style="stroke:navy;stroke-width:1"/>
<circle cx="101" cy="277" r="2" style="stroke:black;stroke-width:1;fill:pink"/>
<line x1="200" y1="200" x2="96" y2="268" style="stroke:navy;stroke-width:1"/>
<circle cx="96" cy="268" r="2" style="stroke:black;stroke-width:1;fill:pink"/>
<line x1="200" y1="200" x2="92" y2="259" style="stroke:navy;stroke-width:1"/>
<circle cx="92" cy="259" r="2" style="stroke:black;stroke-width:1;fill:pink"/>
<line x1="200" y1="200" x2="88" y2="249" style="stroke:navy;stroke-width:1"/>
<circle cx="88" cy="249" r="2" style="stroke:black;stroke-width:1;fill:pink"/>
<line x1="200" y1="200" x2="85" y2="239" style="stroke:navy;stroke-width:1"/>
<circle cx="85" cy="239" r="2" style="stroke:black;stroke-width:1;fill:pink"/>
<line x1="200" y1="200" x2="83" y2="229" style="stroke:navy;stroke-width:1"/>
<circle cx="83" cy="229" r="2" style="stroke:black;stroke-width:1;fill:pink"/>
<line x1="200" y1="200" x2="81" y2="218" style="stroke:navy;stroke-width:1"/>
<circle cx="81" cy="218" r="2" style="stroke:black;stroke-width:1;fill:pink"/>
<line x1="200" y1="200" x2="80" y2="208" style="stroke:navy;stroke-width:1"/>
<circle cx="80" cy="208" r="2" style="stroke:black;stroke-width:1;fill:pink"/>
<line x1="200" y1="200" x2="80" y2="197" style="stroke:navy;stroke-width:1"/>
<circle cx="80" cy="197" r="2" style="stroke:black;stroke-width:1;fill:pink"/>
<line x1="200" y1="200" x2="81" y2="187" style="stroke:navy;stroke-width:1"/>
<circle cx="81" cy="187" r="2" style="stroke:black;stroke-width:1;fill:pink"/>
<line x1="200" y1="200" x2="82" y2="177" style="stroke:navy;stroke-width:1"/>
<circle cx="82" cy="177" r="2" style="stroke:black;stroke-width:1;fill:pink"/>
<line x1="200" y1="200" x2="84" y2="166" style="stroke:navy;stroke-width:1"/>
<circle cx="84" cy="166" r="2" style="stroke:black;stroke-width:1;fill:pink"/>
<line x1="200" y1="200" x2="87" y2="156" style="stroke:navy;stroke-width:1"/>
<circle cx="87" cy="156" r="2" style="stroke:black;stroke-width:1;fill:pink"/>
<line x1="200" y1="200" x2="90" y2="146" style="stroke:navy;stroke-width:1"/>
<circle cx="90" cy="146" r="2" style="stroke:black;stroke-width:1;fill:pink"/>
<line x1="200" y1="200" x2="94" y2="137" style="stroke:navy;stroke-width:1"/>
<circle cx="94" cy="137" r="2" style="stroke:black;stroke-width:1;fill:pink"/>
<line x1="200" y1="200" x2="99" y2="127" style="stroke:navy;stroke-width:1"/>
<circle cx="99" cy="127" r="2" style="stroke:black;stroke-width:1;fill:pink"/>
<line x1="200" y1="200" x2="104" y2="118" style="stroke:navy;stroke-width:1"/>
<circle cx="104" cy="118" r="2" style="stroke:black;stroke-width:1;fill:pink"/>
<line x1="200" y1="200" x2="110" y2="110" style="stroke:navy;stroke-width:1"/>
<circle cx="110" cy="110" r="2" style="stroke:black;stroke-width:1;fill:pink"/>
<line x1="200" y1="200" x2="117" y2="102" style="stroke:navy;stroke-width:1"/>
<circle cx="117" cy="102" r="2" style="stroke:black;stroke-width:1;fill:pink"/>
<line x1="200" y1="200" x2="124" y2="94" style="stroke:navy;stroke-width:1"/>
<circle cx="124" cy="94" r="2" style="stroke:black;stroke-width:1;fill:pink"/>
<line x1="200" y1="200" x2="132" y2="87" style="stroke:navy;stroke-width:1"/>
<circle cx="132" cy="87" r="2" style="stroke:black;stroke-width:1;fill:pink"/>
<line x1="200" y1="200" x2="140" y2="80" style="stroke:navy;stroke-width:1"/>
<circle cx="140" cy="80" r="2" style="stroke:black;stroke-width:1;fill:pink"/>
<line x1="200" y1="200" x2="148" y2="74" style="stroke:navy;stroke-width:1"/>
<circle cx="148" cy="74" r="2" style="stroke:black;stroke-width:1;fill:pink"/>
<line x1="200" y1="200" x2="157" y2="69" style="stroke:navy;stroke-width:1"/>
<circle cx="157" cy="69" r="2" style="stroke:black;stroke-width:1;fill:pink"/>
<line x1="200" y1="200" x2="167" y2="64" style="stroke:navy;stroke-width:1"/>
<circle cx="167" cy="64" r="2" style="stroke:black;stroke-width:1;fill:pink"/>
<line x1="200" y1="200" x2="176" y2="60" style="stroke:navy;stroke-width:1"/>
<circle cx="176" cy="60" r="2" style="stroke:black;stroke-width:1;fill:pink"/>
<line x1="200" y1="200" x2="186" y2="57" style="stroke:navy;stroke-width:1"/>
<circle cx="186" cy="57" r="2" style="stroke:black;stroke-width:1;fill:pink"/>
<line x1="200" y1="200" x2="196" y2="54" style="stroke:navy;stroke-width:1"/>
<circle cx="196" cy="54" r="2" style="stroke:black;stroke-width:1;fill:pink"/>
<line x1="200" y1="200" x2="207" y2="52" style="stroke:navy;stroke-width:1"/>
<circle cx="207" cy="52" r="2" style="stroke:black;stroke-width:1;fill:pink"/>
<line x1="200" y1="200" x2="217" y2="51" style="stroke:navy;stroke-width:1"/>
<circle cx="217" cy="51" r="2" style="stroke:black;stroke-width:1;fill:pink"/>
<line x1="200" y1="200" x2="227" y2="50" style="stroke:navy;stroke-width:1"/>
<circle cx="227" cy="50" r="2" style="stroke:black;stroke-width:1;fill:pink"/>
<line x1="200" y1="200" x2="238" y2="50" style="stroke:navy;stroke-width:1"/>
<circle cx="238" cy="50" r="2" style="stroke:black;stroke-width:1;fill:pink"/>
<line x1="200" y1="200" x2="248" y2="51" style="stroke:navy;stroke-width:1"/>
<circle cx="248" cy="51" r="2" style="stroke:black;stroke-width:1;fill:pink"/>
<line x1="200" y1="200" x2="259" y2="53" style="stroke:navy;stroke-width:1"/>
<circle cx="259" cy="53" r="2" style="stroke:black;stroke-width:1;fill:pink"/>
<line x1="200" y1="200" x2="269" y2="55" style="stroke:navy;stroke-width:1"/>
<circle cx="269" cy="55" r="2" style="stroke:black;stroke-width:1;fill:pink"/>
<line x1="200" y1="200" x2="279" y2="58" style="stroke:navy;stroke-width:1"/>
<circle cx="279" cy="58" r="2" style="stroke:black;stroke-width:1;fill:pink"/>
<line x1="200" y1="200" x2="289" y2="62" style="stroke:navy;stroke-width:1"/>
<circle cx="289" cy="62" r="2" style="stroke:black;stroke-width:1;fill:pink"/>
<line x1="200" y1="200" x2="298" y2="66" style="stroke:navy;stroke-width:1"/>
<circle cx="298" cy="66" r="2" style="stroke:black;stroke-width:1;fill:pink"/>
<line x1="200" y1="200" x2="307" y2="71" style="stroke:navy;stroke-width:1"/>
<circle cx="307" cy="71" r="2" style="stroke:black;stroke-width:1;fill:pink"/>
<line x1="200" y1="200" x2="316" y2="77" style="stroke:navy;stroke-width:1"/>
<circle cx="316" cy="77" r="2" style="stroke:black;stroke-width:1;fill:pink"/>
<line x1="200" y1="200" x2="324" y2="83" style="stroke:navy;stroke-width:1"/>
<circle cx="324" cy="83" r="2" style="stroke:black;stroke-width:1;fill:pink"/>
<line x1="200" y1="200" x2="332" y2="90" style="stroke:navy;stroke-width:1"/>
<circle cx="332" cy="90" r="2" style="stroke:black;stroke-width:1;fill:pink"/>
<line x1="200" y1="200" x2="340" y2="98" style="stroke:navy;stroke-width:1"/>
<circle cx="340" cy="98" r="2" style="stroke:black;stroke-width:1;fill:pink"/>
<line x1="200" y1="200" x2="347" y2="106" style="stroke:navy;stroke-width:1"/>
<circle cx="347" cy="106" r="2" style="stroke:black;stroke-width:1;fill:pink"/>
<line x1="200" y1="200" x2="353" y2="114" style="stroke:navy;stroke-width:1"/>
<circle cx="353" cy="114" r="2" style="stroke:black;stroke-width:1;fill:pink"/>
<line x1="200" y1="200" x2="359" y2="123" style="stroke:navy;stroke-width:1"/>
<circle cx="359" cy="123" r="2" style="stroke:black;stroke-width:1;fill:pink"/>
<line x1="200" y1="200" x2="364" y2="132" style="stroke:navy;stroke-width:1"/>
<circle cx="364" cy="132" r="2" style="stroke:black;stroke-width:1;fill:pink"/>
<line x1="200" y1="200" x2="368" y2="141" style="stroke:navy;stroke-width:1"/>
<circle cx="368" cy="141" r="2" style="stroke:black;stroke-width:1;fill:pink"/>
<line x1="200" y1="200" x2="372" y2="151" style="stroke:navy;stroke-width:1"/>
<circle cx="372" cy="151" r="2" style="stroke:black;stroke-width:1;fill:pink"/>
<line x1="200" y1="200" x2="375" y2="161" style="stroke:navy;stroke-width:1"/>
<circle cx="375" cy="161" r="2" style="stroke:black;stroke-width:1;fill:pink"/>
<line x1="200" y1="200" x2="377" y2="171" style="stroke:navy;stroke-width:1"/>
<circle cx="377" cy="171" r="2" style="stroke:black;stroke-width:1;fill:pink"/>
<line x1="200" y1="200" x2="379" y2="182" style="stroke:navy;stroke-width:1"/>
<circle cx="379" cy="182" r="2" style="stroke:black;stroke-width:1;fill:pink"/>
<line x1="200" y1="200" x2="380" y2="192" style="stroke:navy;stroke-width:1"/>
<circle cx="380" cy="192" r="2" style="stroke:black;stroke-width:1;fill:pink"/>
<circle cx="200" cy="200" r="7" style="stroke:#606060;stroke-width:1;fill:black"/>

<!--    Spoke object 2 -->
<line x1="650" y1="201" x2="750" y2="203" style="stroke:#606060;stroke-width:1;stroke-dasharray:2 4"/>
<rect x="746" y="201" width="8" height="4" transform="rotate(91,750,203)" style="stroke:black;stroke-width:1;fill:pink"/>
<circle cx="649" cy="304" r="2" style="stroke:black;stroke-width:1;fill:red"/>
<line x1="649" y1="208" x2="748" y2="223" style="stroke:#606060;stroke-width:1;stroke-dasharray:2 4"/>
<rect x="744" y="221" width="8" height="4" transform="rotate(99,748,223)" style="stroke:black;stroke-width:1;fill:pink"/>
<circle cx="634" cy="310" r="2" style="stroke:black;stroke-width:1;fill:red"/>
<line x1="648" y1="215" x2="743" y2="244" style="stroke:#606060;stroke-width:1;stroke-dasharray:2 4"/>
<rect x="739" y="242" width="8" height="4" transform="rotate(107,743,244)" style="stroke:black;stroke-width:1;fill:pink"/>
<circle cx="618" cy="314" r="2" style="stroke:black;stroke-width:1;fill:red"/>
<line x1="645" y1="221" x2="736" y2="263" style="stroke:#606060;stroke-width:1;stroke-dasharray:2 4"/>
<rect x="732" y="261" width="8" height="4" transform="rotate(115,736,263)" style="stroke:black;stroke-width:1;fill:pink"/>
<circle cx="602" cy="315" r="2" style="stroke:black;stroke-width:1;fill:red"/>
<line x1="642" y1="227" x2="726" y2="282" style="stroke:#606060;stroke-width:1;stroke-dasharray:2 4"/>
<rect x="722" y="280" width="8" height="4" transform="rotate(123,726,282)" style="stroke:black;stroke-width:1;fill:pink"/>
<circle cx="586" cy="314" r="2" style="stroke:black;stroke-width:1;fill:red"/>
<line x1="638" y1="233" x2="713" y2="298" style="stroke:#606060;stroke-width:1;stroke-dasharray:2 4"/>
<rect x="709" y="296" width="8" height="4" transform="rotate(131,713,298)" style="stroke:black;stroke-width:1;fill:pink"/>
<circle cx="570" cy="311" r="2" style="stroke:black;stroke-width:1;fill:red"/>
<line x1="633" y1="238" x2="698" y2="313" style="stroke:#606060;stroke-width:1;stroke-dasharray:2 4"/>
<rect x="694" y="311" width="8" height="4" transform="rotate(139,698,313)" style="stroke:black;stroke-width:1;fill:pink"/>
<circle cx="555" cy="306" r="2" style="stroke:black;stroke-width:1;fill:red"/>
<line x1="627" y1="242" x2="682" y2="326" style="stroke:#606060;stroke-width:1;stroke-dasharray:2 4"/>
<rect x="678" y="324" width="8" height="4" transform="rotate(147,682,326)" style="stroke:black;stroke-width:1;fill:pink"/>
<circle cx="541" cy="299" r="2" style="stroke:black;stroke-width:1;fill:red"/>
<line x1="621" y1="245" x2="663" y2="336" style="stroke:#606060;stroke-width:1;stroke-dasharray:2 4"/>
<rect x="659" y="334" width="8" height="4" transform="rotate(155,663,336)" style="stroke:black;stroke-width:1;fill:pink"/>
<circle cx="528" cy="289" r="2" style="stroke:black;stroke-width:1;fill:red"/>
<line x1="615" y1="248" x2="644" y2="343" style="stroke:#606060;stroke-width:1;stroke-dasharray:2 4"/>
<rect x="640" y="341" width="8" height="4" transform="rotate(163,644,343)" style="stroke:black;stroke-width:1;fill:pink"/>
<circle cx="516" cy="278" r="2" style="stroke:black;stroke-width:1;fill:red"/>
<line x1="608" y1="249" x2="623" y2="348" style="stroke:#606060;stroke-width:1;stroke-dasharray:2 4"/>
<rect x="619" y="346" width="8" height="4" transform="rotate(171,623,348)" style="stroke:black;stroke-width:1;fill:pink"/>
<circle cx="506" cy="266" r="2" style="stroke:black;stroke-width:1;fill:red"/>
<line x1="601" y1="250" x2="603" y2="350" style="stroke:#606060;stroke-width:1;stroke-dasharray:2 4"/>
<rect x="599" y="348" width="8" height="4" transform="rotate(179,603,350)" style="stroke:black;stroke-width:1;fill:pink"/>
<circle cx="498" cy="252" r="2" style="stroke:black;stroke-width:1;fill:red"/>
<line x1="594" y1="250" x2="582" y2="349" style="stroke:#606060;stroke-width:1;stroke-dasharray:2 4"/>
<rect x="578" y="347" width="8" height="4" transform="rotate(187,582,349)" style="stroke:black;stroke-width:1;fill:pink"/>
<circle cx="491" cy="237" r="2" style="stroke:black;stroke-width:1;fill:red"/>
<line x1="587" y1="248" x2="561" y2="345" style="stroke:#606060;stroke-width:1;stroke-dasharray:2 4"/>
<rect x="557" y="343" width="8" height="4" transform="rotate(195,561,345)" style="stroke:black;stroke-width:1;fill:pink"/>
<circle cx="487" cy="222" r="2" style="stroke:black;stroke-width:1;fill:red"/>
<line x1="580" y1="246" x2="541" y2="338" style="stroke:#606060;stroke-width:1;stroke-dasharray:2 4"/>
<rect x="537" y="336" width="8" height="4" transform="rotate(203,541,338)" style="stroke:black;stroke-width:1;fill:pink"/>
<circle cx="485" cy="206" r="2" style="stroke:black;stroke-width:1;fill:red"/>
<line x1="574" y1="243" x2="523" y2="329" style="stroke:#606060;stroke-width:1;stroke-dasharray:2 4"/>
<rect x="519" y="327" width="8" height="4" transform="rotate(211,523,329)" style="stroke:black;stroke-width:1;fill:pink"/>
<circle cx="485" cy="190" r="2" style="stroke:black;stroke-width:1;fill:red"/>
<line x1="569" y1="239" x2="506" y2="317" style="stroke:#606060;stroke-width:1;stroke-dasharray:2 4"/>
<rect x="502" y="315" width="8" height="4" transform="rotate(219,506,317)" style="stroke:black;stroke-width:1;fill:pink"/>
<circle cx="488" cy="174" r="2" style="stroke:black;stroke-width:1;fill:red"/>
<line x1="563" y1="234" x2="490" y2="302" style="stroke:#606060;stroke-width:1;stroke-dasharray:2 4"/>
<rect x="486" y="300" width="8" height="4" transform="rotate(227,490,302)" style="stroke:black;stroke-width:1;fill:pink"/>
<circle cx="493" cy="159" r="2" style="stroke:black;stroke-width:1;fill:red"/>
<line x1="559" y1="229" x2="477" y2="286" style="stroke:#606060;stroke-width:1;stroke-dasharray:2 4"/>
<rect x="473" y="284" width="8" height="4" transform="rotate(235,477,286)" style="stroke:black;stroke-width:1;fill:pink"/>
<circle cx="499" cy="144" r="2" style="stroke:black;stroke-width:1;fill:red"/>
<line x1="555" y1="223" x2="466" y2="268" style="stroke:#606060;stroke-width:1;stroke-dasharray:2 4"/>
<rect x="462" y="266" width="8" height="4" transform="rotate(243,466,268)" style="stroke:black;stroke-width:1;fill:pink"/>
<circle cx="508" cy="131" r="2" style="stroke:black;stroke-width:1;fill:red"/>
<line x1="553" y1="216" x2="458" y2="249" style="stroke:#606060;stroke-width:1;stroke-dasharray:2 4"/>
<rect x="454" y="247" width="8" height="4" transform="rotate(251,458,249)" style="stroke:black;stroke-width:1;fill:pink"/>
<circle cx="519" cy="119" r="2" style="stroke:black;stroke-width:1;fill:red"/>
<line x1="551" y1="210" x2="453" y2="229" style="stroke:#606060;stroke-width:1;stroke-dasharray:2 4"/>
<rect x="449" y="227" width="8" height="4" transform="rotate(259,453,229)" style="stroke:black;stroke-width:1;fill:pink"/>
<circle cx="531" cy="108" r="2" style="stroke:black;stroke-width:1;fill:red"/>
<line x1="550" y1="203" x2="450" y2="208" style="stroke:#606060;stroke-width:1;stroke-dasharray:2 4"/>
<rect x="446" y="206" width="8" height="4" transform="rotate(267,450,208)" style="stroke:black;stroke-width:1;fill:pink"/>
<circle cx="544" cy="99" r="2" style="stroke:black;stroke-width:1;fill:red"/>
<line x1="550" y1="196" x2="451" y2="187" style="stroke:#606060;stroke-width:1;stroke-dasharray:2 4"/>
<rect x="447" y="185" width="8" height="4" transform="rotate(275,451,187)" style="stroke:black;stroke-width:1;fill:pink"/>
<circle cx="559" cy="93" r="2" style="stroke:black;stroke-width:1;fill:red"/>
<line x1="551" y1="189" x2="454" y2="166" style="stroke:#606060;stroke-width:1;stroke-dasharray:2 4"/>
<rect x="450" y="164" width="8" height="4" transform="rotate(283,454,166)" style="stroke:black;stroke-width:1;fill:pink"/>
<circle cx="574" cy="88" r="2" style="stroke:black;stroke-width:1;fill:red"/>
<line x1="553" y1="182" x2="460" y2="146" style="stroke:#606060;stroke-width:1;stroke-dasharray:2 4"/>
<rect x="456" y="144" width="8" height="4" transform="rotate(291,460,146)" style="stroke:black;stroke-width:1;fill:pink"/>
<circle cx="590" cy="85" r="2" style="stroke:black;stroke-width:1;fill:red"/>
<line x1="556" y1="176" x2="469" y2="127" style="stroke:#606060;stroke-width:1;stroke-dasharray:2 4"/>
<rect x="465" y="125" width="8" height="4" transform="rotate(299,469,127)" style="stroke:black;stroke-width:1;fill:pink"/>
<circle cx="606" cy="85" r="2" style="stroke:black;stroke-width:1;fill:red"/>
<line x1="560" y1="170" x2="480" y2="110" style="stroke:#606060;stroke-width:1;stroke-dasharray:2 4"/>
<rect x="476" y="108" width="8" height="4" transform="rotate(307,480,110)" style="stroke:black;stroke-width:1;fill:pink"/>
<circle cx="622" cy="87" r="2" style="stroke:black;stroke-width:1;fill:red"/>
<line x1="565" y1="165" x2="494" y2="94" style="stroke:#606060;stroke-width:1;stroke-dasharray:2 4"/>
<rect x="490" y="92" width="8" height="4" transform="rotate(315,494,94)" style="stroke:black;stroke-width:1;fill:pink"/>
<circle cx="637" cy="91" r="2" style="stroke:black;stroke-width:1;fill:red"/>
<line x1="570" y1="160" x2="510" y2="80" style="stroke:#606060;stroke-width:1;stroke-dasharray:2 4"/>
<rect x="506" y="78" width="8" height="4" transform="rotate(323,510,80)" style="stroke:black;stroke-width:1;fill:pink"/>
<circle cx="652" cy="98" r="2" style="stroke:black;stroke-width:1;fill:red"/>
<line x1="576" y1="156" x2="527" y2="69" style="stroke:#606060;stroke-width:1;stroke-dasharray:2 4"/>
<rect x="523" y="67" width="8" height="4" transform="rotate(331,527,69)" style="stroke:black;stroke-width:1;fill:pink"/>
<circle cx="666" cy="106" r="2" style="stroke:black;stroke-width:1;fill:red"/>
<line x1="582" y1="153" x2="546" y2="60" style="stroke:#606060;stroke-width:1;stroke-dasharray:2 4"/>
<rect x="542" y="58" width="8" height="4" transform="rotate(339,546,60)" style="stroke:black;stroke-width:1;fill:pink"/>
<circle cx="678" cy="116" r="2" style="stroke:black;stroke-width:1;fill:red"/>
<line x1="589" y1="151" x2="566" y2="54" style="stroke:#606060;stroke-width:1;stroke-dasharray:2 4"/>
<rect x="562" y="52" width="8" height="4" transform="rotate(347,566,54)" style="stroke:black;stroke-width:1;fill:pink"/>
<circle cx="689" cy="128" r="2" style="stroke:black;stroke-width:1;fill:red"/>
<line x1="596" y1="150" x2="587" y2="51" style="stroke:#606060;stroke-width:1;stroke-dasharray:2 4"/>
<rect x="583" y="49" width="8" height="4" transform="rotate(355,587,51)" style="stroke:black;stroke-width:1;fill:pink"/>
<circle cx="699" cy="141" r="2" style="stroke:black;stroke-width:1;fill:red"/>
<line x1="603" y1="150" x2="608" y2="50" style="stroke:#606060;stroke-width:1;stroke-dasharray:2 4"/>
<rect x="604" y="48" width="8" height="4" transform="rotate(363,608,50)" style="stroke:black;stroke-width:1;fill:pink"/>
<circle cx="706" cy="155" r="2" style="stroke:black;stroke-width:1;fill:red"/>
<line x1="610" y1="151" x2="629" y2="53" style="stroke:#606060;stroke-width:1;stroke-dasharray:2 4"/>
<rect x="625" y="51" width="8" height="4" transform="rotate(371,629,53)" style="stroke:black;stroke-width:1;fill:pink"/>
<circle cx="711" cy="170" r="2" style="stroke:black;stroke-width:1;fill:red"/>
<line x1="616" y1="153" x2="649" y2="58" style="stroke:#606060;stroke-width:1;stroke-dasharray:2 4"/>
<rect x="645" y="56" width="8" height="4" transform="rotate(379,649,58)" style="stroke:black;stroke-width:1;fill:pink"/>
<circle cx="714" cy="186" r="2" style="stroke:black;stroke-width:1;fill:red"/>
<line x1="623" y1="155" x2="668" y2="66" style="stroke:#606060;stroke-width:1;stroke-dasharray:2 4"/>
<rect x="664" y="64" width="8" height="4" transform="rotate(387,668,66)" style="stroke:black;stroke-width:1;fill:pink"/>
<circle cx="715" cy="202" r="2" style="stroke:black;stroke-width:1;fill:red"/>
<line x1="629" y1="159" x2="686" y2="77" style="stroke:#606060;stroke-width:1;stroke-dasharray:2 4"/>
<rect x="682" y="75" width="8" height="4" transform="rotate(395,686,77)" style="stroke:black;stroke-width:1;fill:pink"/>
<circle cx="714" cy="218" r="2" style="stroke:black;stroke-width:1;fill:red"/>
<line x1="634" y1="163" x2="702" y2="90" style="stroke:#606060;stroke-width:1;stroke-dasharray:2 4"/>
<rect x="698" y="88" width="8" height="4" transform="rotate(403,702,90)" style="stroke:black;stroke-width:1;fill:pink"/>
<circle cx="710" cy="234" r="2" style="stroke:black;stroke-width:1;fill:red"/>
<line x1="639" y1="169" x2="717" y2="106" style="stroke:#606060;stroke-width:1;stroke-dasharray:2 4"/>
<rect x="713" y="104" width="8" height="4" transform="rotate(411,717,106)" style="stroke:black;stroke-width:1;fill:pink"/>
<circle cx="704" cy="249" r="2" style="stroke:black;stroke-width:1;fill:red"/>
<line x1="643" y1="174" x2="729" y2="123" style="stroke:#606060;stroke-width:1;stroke-dasharray:2 4"/>
<rect x="725" y="121" width="8" height="4" transform="rotate(419,729,123)" style="stroke:black;stroke-width:1;fill:pink"/>
<circle cx="696" cy="263" r="2" style="stroke:black;stroke-width:1;fill:red"/>
<line x1="646" y1="180" x2="738" y2="141" style="stroke:#606060;stroke-width:1;stroke-dasharray:2 4"/>
<rect x="734" y="139" width="8" height="4" transform="rotate(427,738,141)" style="stroke:black;stroke-width:1;fill:pink"/>
<circle cx="687" cy="275" r="2" style="stroke:black;stroke-width:1;fill:red"/>
<line x1="648" y1="187" x2="745" y2="161" style="stroke:#606060;stroke-width:1;stroke-dasharray:2 4"/>
<rect x="741" y="159" width="8" height="4" transform="rotate(435,745,161)" style="stroke:black;stroke-width:1;fill:pink"/>
<circle cx="675" cy="287" r="2" style="stroke:black;stroke-width:1;fill:red"/>
<line x1="650" y1="194" x2="749" y2="182" style="stroke:#606060;stroke-width:1;stroke-dasharray:2 4"/>
<rect x="745" y="180" width="8" height="4" transform="rotate(443,749,182)" style="stroke:black;stroke-width:1;fill:pink"/>
<circle cx="663" cy="296" r="2" style="stroke:black;stroke-width:1;fill:red"/>

<!--    Quad Bezier in Path -->
<path d="M 650,201 Q 649,304 750,203
         M 649,208 Q 634,310 748,223
         M 648,215 Q 618,314 743,244
         M 645,221 Q 602,315 736,263
         M 642,227 Q 586,314 726,282
         M 638,233 Q 570,311 713,298
         M 633,238 Q 555,306 698,313
         M 627,242 Q 541,299 682,326
         M 621,245 Q 528,289 663,336
         M 615,248 Q 516,278 644,343
         M 608,249 Q 506,266 623,348
         M 601,250 Q 498,252 603,350
         M 594,250 Q 491,237 582,349
         M 587,248 Q 487,222 561,345
         M 580,246 Q 485,206 541,338
         M 574,243 Q 485,190 523,329
         M 569,239 Q 488,174 506,317
         M 563,234 Q 493,159 490,302
         M 559,229 Q 499,144 477,286
         M 555,223 Q 508,131 466,268
         M 553,216 Q 519,119 458,249
         M 551,210 Q 531,108 453,229
         M 550,203 Q 544,99 450,208
         M 550,196 Q 559,93 451,187
         M 551,189 Q 574,88 454,166
         M 553,182 Q 590,85 460,146
         M 556,176 Q 606,85 469,127
         M 560,170 Q 622,87 480,110
         M 565,165 Q 637,91 494,94
         M 570,160 Q 652,98 510,80
         M 576,156 Q 666,106 527,69
         M 582,153 Q 678,116 546,60
         M 589,151 Q 689,128 566,54
         M 596,150 Q 699,141 587,51
         M 603,150 Q 706,155 608,50
         M 610,151 Q 711,170 629,53
         M 616,153 Q 714,186 649,58
         M 623,155 Q 715,202 668,66
         M 629,159 Q 714,218 686,77
         M 634,163 Q 710,234 702,90
         M 639,169 Q 704,249 717,106
         M 643,174 Q 696,263 729,123
         M 646,180 Q 687,275 738,141
         M 648,187 Q 675,287 745,161
         M 650,194 Q 663,296 749,182"
 style="stroke:navy;stroke-width:1;fill:none"/>

<circle cx="600" cy="200" r="50" style="stroke:navy;stroke-width:2;fill:none"/>
<text x="430" y="25"  style="font-family:verdana;font-size:18px;Font-Weight:bold;fill:navy;text-anchor:middle">Points On a Circle </text>
<text x="200" y="400"  style="font-family:verdana;font-size:12px;Font-Weight:bold;fill:navy;text-anchor:middle">Line spokes </text>
<text x="600" y="400"  style="font-family:verdana;font-size:12px;Font-Weight:bold;fill:navy;text-anchor:middle">Quad Bezier curves using Path </text>
<text x="600" y="420"  style="font-family:verdana;font-size:10px;Font-Weight:bold;fill:navy;text-anchor:middle">Red dot is the Bezier handle(from another spoke) </text>

</svg>

</body>
</html>

Image
Post Reply