Vector Curve Designer (RAD Tool)

Share your advanced PureBasic knowledge/code with the community.
User avatar
netmaestro
PureBasic Bullfrog
PureBasic Bullfrog
Posts: 8422
Joined: Wed Jul 06, 2005 5:42 am
Location: Fort Nelson, BC, Canada

Vector Curve Designer (RAD Tool)

Post by netmaestro »

For a quickstart to understanding what this project is about, have a look at this video: https://www.youtube.com/watch?v=4VbWBMsiaqU

The program is for Windows but really the only part that's OS specific is the cursor. If you place the cursor code in a CompilerIf block and put in code for MacOS and Linux cursor creation, it should be cross-platform. Or you could just comment the cursor stuff out and use the default arrow and you're good to go.

*Update: Idle and wombats helped out with the cursors so I think we're cross-platform now.

You need to know: On your first click you get the first control point for a new curve. Move away and click again you now have a straight line with four control points. Move the control points all around until you're happy with the curve and then click "commit" button. Go to the end of where you want your next curve to be and click. You now have a new curve that started where the last one ended. Keep doing that as you go around your shape until you need a new curve that doesn't start where the last one ended. Away from everything. Go to the controls on the left and select "independent curve". Now your next curve will start just like the first one did wherever you click. For an example you can watch the video, which is worth a thousand words.

*NOTE: I had an API call in the code, just the one to create the cursor but I realized if I switch it for an import and conditionally create the structure this would run on the free version. So I did and it runs fine. Maybe it'll attract some registrations, who knows? It makes pretty cool drawings quickly with hardly any work.

*Edit: I just hit 800 lines so I guess that idea's history.

*Update May 16:
- The interface is completely redesigned to be more intuitive and to leave maximum screen area available for editing.
- Full resizing of the window is implemented, so you can work at any size that you're comfortable with and have the screen real estate for.
- Undo / Redo is implemented.
- Updates happen in real time. For example, if you select code view and toggle between fixed size and proportional you can watch the code change. Same with undo and redo, you can watch the code lines disappear and reappear as they get undone and redone.
- Test view now shows the curves you've made in their native size rather than whatever size you've got the designer zoomed to. (I usually use maximized)
- Made a new video to show the new interface and features. Link is updated.

*Update May 18:
Code is usable now as IDE tool on Windows OS. Just compile it to exe, add it as external tool to your IDE and you have an "Insert to IDE" button now. Here's my simple tool config:

*Update May 22:
Zoom feature is implemented for doing tiny work, this is enough features for now so if no serious bugs are reported in a few days the project will be at beta 1.0

Image

Place your cursor inside a StartVectorDrawing block, hit your shortcut, design your curve, hit the insert button and you're done. Here's a snippet of test code:

Code: Select all

sz.d=708 ; initial size of canvas if you don't load an image
CreateImage(0,sz,sz,24,#White)
StartVectorDrawing(ImageVectorOutput(0))
  
  StrokePath(8, #PB_Path_RoundCorner|#PB_Path_RoundEnd)
StopVectorDrawing()

OpenWindow(0,0,0,sz,sz,"",#PB_Window_ScreenCentered|#PB_Window_SystemMenu)
ImageGadget(0,0,0,sz,sz,ImageID(0))
Repeat:Until WaitWindowEvent()=#PB_Event_CloseWindow
Here is the code, looking for lots of suggestions for features and code additions/modifications welcome:

Code: Select all

;///////////////////////////////////////////////////
; Project:      Curve Designer
; Author:       Lloyd Gallant (netmaestro)
; Contributors: Idle (Linux cursor creation)
;               wombats (MacOS cursor creation)
;               Version 1.1  Jan 27, 2023 (DrShrek aka IceSoft)
;               Paul (DPI Aware bugfix)
; Date:         May 14,2020, last update (bugfix): April 18, 2022
;               Jan 27, 2023 (DrShek aka IceSoft)
; Target OS:    Windows
; Compiler:     PureBasic 6.0 
; License:      Unrestricted, do as you like
; What it is:   WYSIWYG Vector curve designer
;               (RAD tool for quick drawing)
; Version:      1.1.01 
;////////////////////////////////////////////////////

EnableExplicit

CompilerIf #PB_Compiler_OS = #PB_OS_Windows
  
  CompilerIf Defined(ICONINFO, #PB_Structure)=0
    Structure ICONINFO
      fIcon.l
      xHotspot.l
      yHotspot.l
      hbmMask.i
      hbmColor.i
    EndStructure
  CompilerEndIf
  
  Import "user32.lib"
    CreateIconIndirect(*lptICONINFO)
  EndImport
  
CompilerEndIf

Declare NewImageButtonHandler()
Declare CanvasHandler()
Declare CreateCursor()
Declare UpdateCanvas()
Declare RedrawCurves()
Declare OptionHandler()
Declare StringHandler()
Declare New()
Declare CommitHandler()
Declare CloseCurveHandler()
Declare CopyHandler()
Declare LogCurves()
Declare ResizeHandler()
Declare ConvertCurvesToOutput()
Declare ConvertCurvesFromOutput()
Declare InitializeCanvas()
Declare UndoHandler()
Declare RedoHandler()
Declare ClearHandler()

CompilerIf #PB_Compiler_OS = #PB_OS_Windows
  
  Declare InsertHandler()
  
CompilerEndIf

Declare ZoomHandler()
Declare ScrollHandler()


Structure pointd
  x.d
  y.d
EndStructure

Structure curve
  moverequired.i
  startpoint.pointd
  firstpoint.pointd
  secondpoint.pointd
  endpoint.pointd
EndStructure

; Control points
Global.pointd startpoint,firstpoint,secondpoint,endpoint,oldstartpoint,oldfirstpoint,oldsecondpoint,oldendpoint
Global.curve *last, *first

; Dimensions
Global.d width_viewport,height_viewport,oldwidth_viewport,oldheight_viewport,oldcx,oldcy,outputcx,outputcy
Global.d max_viewport,max_zoom=1800,width_zoom,height_zoom,szIcon
Global Dim cx.d(1)
Global Dim cy.d(1)
Global Dim cxborder.d(1)
Global Dim cyborder.d(1)

; Images
Global.i imgCursor=CreateCursor(),imgOriginalBackground,imgOutput,imgZoomed,imgZoomNormal,imgZoomViewport,imgUndo,imgRedo,imgClear
Global Dim imgBackgrounds.i(1)
Global Dim imgLayers.i(1)

; Gadgets
Global.i gadget_output,option1,option2,option3,option4,option5,option6,option7,gadget_cx, gadget_cy,label_cx,label_cy,canvas
Global.i testcanvas,ccontainer,button_commit,button_closeCurve,button_newimage,button_copy,gadget_undo,gadget_redo,gadget_clear,button_insert
Global.i gadget_zoom,gadget_zoom_viewport

; Logic Flow variables
Global.i moveinprogress,curveactive,newcurve,on1,on2,on3,on4,zoomlevel=0

; Fonts
Define.i output_font

Global NewList codelines.s()
Global NewList curves.curve()
Global NewList displaycurves.curve()
Global NewList trashcurves.curve()
Global NewList trashdisplaycurves.curve()

OpenWindow(0,0,0,1024,710,"Vector Curve Designer Version 1.1",#PB_Window_ScreenCentered |
                                                              #PB_Window_SystemMenu        |
                                                              #PB_Window_SizeGadget        |
                                                              #PB_Window_MinimizeGadget    |
                                                              #PB_Window_MaximizeGadget)
StickyWindow(0,1)
WindowBounds(0,780,470,#PB_Default,#PB_Default)
width_viewport=WindowHeight(0)-10
height_viewport=width_viewport
BindEvent(#PB_Event_SizeWindow, @ResizeHandler())

; Left side control panel
szIcon=24*DesktopResolutionX()
imgUndo=CreateImage(#PB_Any,szIcon,szIcon,32,#PB_Image_Transparent)
imgRedo=CreateImage(#PB_Any, szIcon,szIcon,32,#PB_Image_Transparent)
StartVectorDrawing(ImageVectorOutput(imgUndo))
VectorSourceColor(RGBA(255,255,255,255))
FillVectorOutput()
VectorSourceColor(RGBA(0,0,0,255))
AddPathCircle(0.5000*szIcon,0.6000*szIcon,0.2100*szIcon,40,240,#PB_Path_CounterClockwise)
SaveVectorState()     
RotateCoordinates(0.5000*szIcon,0.5000*szIcon, PathPointAngle(PathLength()))
AddPathLine(0.0000*szIcon,-0.1000*szIcon, #PB_Path_Relative)
AddPathLine(0.1333*szIcon,0.0667*szIcon, #PB_Path_Relative)
AddPathLine(-0.1333*szIcon,0.1000*szIcon, #PB_Path_Relative)
AddPathLine(0.0000*szIcon,-0.1000*szIcon,#PB_Path_Relative)
RestoreVectorState()   
StrokePath(0.0933*szIcon)
StopVectorDrawing()

StartVectorDrawing(ImageVectorOutput(imgRedo))
FlipCoordinatesX(0.5000*szIcon)
MovePathCursor(0,0)
DrawVectorImage(ImageID(imgUndo))
StopVectorDrawing()

imgZoomNormal=CreateImage(#PB_Any,szIcon,szIcon,24,#White)
StartVectorDrawing(ImageVectorOutput(imgZoomNormal))
AddPathCircle(0.4667*szIcon,0.4667*szIcon,0.3000*szIcon)
StrokePath(0.0800*szIcon)
MovePathCursor(0.7167*szIcon,0.7167*szIcon)
AddPathLine(0.8667*szIcon,0.8667*szIcon)
VectorSourceColor(RGBA(0,0,0,255))
StrokePath(0.2000*szIcon,#PB_Path_RoundCorner|#PB_Path_RoundEnd)
MovePathCursor(0.4667*szIcon,0.3333*szIcon)
AddPathLine(0.4667*szIcon,0.6000*szIcon)
MovePathCursor(0.3333*szIcon,0.4667*szIcon)
AddPathLine(0.6000*szIcon,0.4667*szIcon)
StrokePath(0.0800*szIcon,#PB_Path_RoundCorner|#PB_Path_RoundEnd)
StopVectorDrawing()

imgZoomed=CreateImage(#PB_Any,szIcon,szIcon,24,#White)
StartVectorDrawing(ImageVectorOutput(imgZoomed))
VectorSourceColor(RGBA(255,0,0,255))
AddPathCircle(0.4667*szIcon,0.4667*szIcon,0.3000*szIcon)
StrokePath(0.0800*szIcon)
MovePathCursor(0.7167*szIcon,0.7167*szIcon)
AddPathLine(0.8667*szIcon,0.8667*szIcon)
StrokePath(0.2000*szIcon,#PB_Path_RoundCorner|#PB_Path_RoundEnd)
MovePathCursor(0.4667*szIcon,0.3333*szIcon)
AddPathLine(0.4667*szIcon,0.6000*szIcon)
MovePathCursor(0.3333*szIcon,0.4667*szIcon)
AddPathLine(0.6000*szIcon,0.4667*szIcon)
StrokePath(0.0800*szIcon,#PB_Path_RoundCorner|#PB_Path_RoundEnd)
StopVectorDrawing()

imgClear=CreateImage(#PB_Any,szIcon,szIcon,24,#White)
StartVectorDrawing(ImageVectorOutput(imgZoomed))
VectorSourceColor(RGBA(255,0,0,255))
StrokePath(0.0800*szIcon,#PB_Path_RoundCorner|#PB_Path_RoundEnd)
StopVectorDrawing()

FrameGadget(#PB_Any,10,10,270,36,"",#PB_Frame_Flat)
gadget_undo=ButtonImageGadget(#PB_Any, 12,12,32,32,ImageID(imgUndo))
gadget_redo=ButtonImageGadget(#PB_Any, 44,12,32,32,ImageID(imgRedo))
gadget_zoom=ButtonImageGadget(#PB_Any, 78,12,32,32,ImageID(imgZoomNormal),#PB_Button_Toggle)
gadget_clear=ButtonImageGadget(#PB_Any, 110,12,32,32,ImageID(imgClear))
BindGadgetEvent(gadget_undo, @UndoHandler())
BindGadgetEvent(gadget_redo, @RedoHandler())
BindGadgetEvent(gadget_zoom, @ZoomHandler())
BindGadgetEvent(gadget_clear, @ClearHandler())

button_newimage=ButtonGadget(#PB_Any, 10,50,270,20,"Load Image")
BindGadgetEvent(button_newimage, @NewImageButtonHandler())
FrameGadget(#PB_Any, 10,85,270,80,"Output Type")
option1=OptionGadget(#PB_Any,40,110,90,20,"Fixed size")
option2=OptionGadget(#PB_Any,40,130,90,20,"proportional")
SetGadgetState(option1,1)
BindGadgetEvent(option1,@OptionHandler())
BindGadgetEvent(option2,@OptionHandler())
label_cx=TextGadget(#PB_Any, 140,112,15,20,"W:")
gadget_cx=StringGadget(#PB_Any,158,110,35,20,"")
label_cy=TextGadget(#PB_Any, 204,112,15,20,"H:")
gadget_cy=StringGadget(#PB_Any,220,110,35,20,"")
DisableGadget(gadget_cx, 0)
DisableGadget(gadget_cy, 0)
DisableGadget(label_cx, 0)
DisableGadget(label_cy, 0)
BindGadgetEvent(gadget_cx, @StringHandler())
BindGadgetEvent(gadget_cy, @StringHandler())
FrameGadget(#PB_Any, 10,180,270,90,"View")
option3=OptionGadget(#PB_Any,40,200,90,20,"Design View")
option4=OptionGadget(#PB_Any,40,220,90,20,"Code View")
option5=OptionGadget(#PB_Any,40,240,90,20,"Test View")
BindGadgetEvent(option3, @OptionHandler())
BindGadgetEvent(option4, @OptionHandler())
BindGadgetEvent(option5, @OptionHandler())
SetGadgetState(option3, 1)
FrameGadget(#PB_Any, 10,290,270,80,"Curve Type")
option6=OptionGadget(#PB_Any,40,310,190,20,"Independent curve")
option7=OptionGadget(#PB_Any,40,330,190,20,"Continuation of last curve")
BindGadgetEvent(option6, @OptionHandler())
BindGadgetEvent(option7, @OptionHandler())
SetGadgetState(option6,1)
button_commit=ButtonGadget(#PB_Any, 10,380,135,20,"Commit This Curve")
DisableGadget(button_commit, 1)
BindGadgetEvent(button_commit, @CommitHandler())
button_closeCurve=ButtonGadget(#PB_Any, 10+135,380,135,20,"Connect To First Curve")
DisableGadget(button_closeCurve, 1)
BindGadgetEvent(button_closeCurve, @CloseCurveHandler())
button_copy=ButtonGadget(#PB_Any, 10,410,270,20,"Copy Code To Clipboard")
BindGadgetEvent(button_copy, @CopyHandler())

CompilerIf #PB_Compiler_OS = #PB_OS_Windows
  
  button_insert=ButtonGadget(#PB_Any, 10,440,270,20,"Insert Code To IDE")
  BindGadgetEvent(button_insert, @InsertHandler())
  
CompilerEndIf

gadget_zoom_viewport = ImageGadget(#PB_Any,60,480,0,0,0,#PB_Image_Raised)
HideGadget(gadget_zoom_viewport, 1)

; Edit window
ccontainer=ScrollAreaGadget(#PB_Any,315,0,702,702,700,700,10,#PB_ScrollArea_Flat)
BindGadgetEvent(ccontainer, @ScrollHandler())
canvas=CanvasGadget(#PB_Any,0,0,700,700,#PB_Canvas_Keyboard|#PB_Canvas_ClipMouse)
CloseGadgetList()
BindGadgetEvent(canvas, @CanvasHandler())
testcanvas=CanvasGadget(#PB_Any,321,1,WindowHeight(0)-2,WindowHeight(0)-2)
output_font=LoadFont(#PB_Any, "consolas", 11)
gadget_output = EditorGadget(#PB_Any, 316,1,700,700)
SetGadgetFont(gadget_output, FontID(output_font))
HideGadget(gadget_output,1)
HideGadget(testcanvas,1)
imgOriginalBackground=CreateImage(#PB_Any, WindowHeight(0)-2,WindowHeight(0)-2,24)
InitializeCanvas()

SetActiveGadget(canvas)

Repeat:Until WaitWindowEvent()=#PB_Event_CloseWindow

;===============================================
;              PROCEDURE SECTION
;===============================================

Procedure ScrollHandler()
  Protected.d cxsmall,cysmall,x,y
  Protected.i imgZoomViewportOutput
  If Not IsImage(imgZoomViewport)
    imgZoomViewport=CreateImage(#PB_Any, ImageWidth(imgBackgrounds(0))*0.2,ImageHeight(imgBackgrounds(0))*0.2)
    StartVectorDrawing(ImageVectorOutput(imgZoomViewport))
    MovePathCursor(0,0)
    DrawVectorImage(ImageID(imgBackgrounds(0)),255,ImageWidth(imgZoomViewport),ImageHeight(imgZoomViewport))
    StopVectorDrawing()
    ResizeGadget(gadget_zoom_viewport,#PB_Ignore,#PB_Ignore,ImageWidth(imgZoomViewport),ImageHeight(imgZoomViewport))
    SetGadgetState(gadget_zoom_viewport, ImageID(imgZoomViewport))
  EndIf
  
  cxsmall.d = ImageWidth(imgZoomViewport)
  cysmall.d = ImageHeight(imgZoomViewport)
  x.d = GetGadgetAttribute(ccontainer, #PB_ScrollArea_X)
  y.d = GetGadgetAttribute(ccontainer, #PB_ScrollArea_Y)
  imgZoomViewportOutput = CopyImage(imgZoomViewport, #PB_Any)
  StartVectorDrawing(ImageVectorOutput(imgZoomViewportOutput))
  MovePathCursor(0,0)
  DrawVectorImage(ImageID(imgLayers(1)),255,cxsmall,cysmall)   
  AddPathBox(x/ImageWidth(imgBackgrounds(1))*cxsmall+1,
             y/ImageHeight(imgBackgrounds(1))*cysmall+1,
             width_viewport/ImageWidth(imgBackgrounds(1))*cxsmall,
             height_viewport/ImageHeight(imgBackgrounds(1))*cysmall)
  VectorSourceColor(RGBA(255,0,0,255))
  StrokePath(1)
  StopVectorDrawing()
  SetGadgetState(gadget_zoom_viewport, ImageID(imgZoomViewportOutput))
  
EndProcedure

CompilerIf #PB_Compiler_OS = #PB_OS_Windows
  
  Procedure InsertHandler()
    Protected.i scintilla
    Protected *textBuffer
    scintilla = Val(GetEnvironmentVariable("PB_Tool_Scintilla"))
    If Not IsWindowVisible_(scintilla)
      MessageRequester("Notice","You can only add the generated code"+#CRLF$             +
                                "to the file you launched the tool from."+#CRLF$+#CRLF$  +
                                "To deposit the code elsewhere, use the"+#CRLF$          +
                                "copy button and paste the code manually")
    Else
      If scintilla
        *textBuffer = UTF8(#CRLF$+"; Code inserted by Vector Curve Designer" +
                           #CRLF$+GetGadgetText(gadget_output)+#CRLF$ +
                           "; End of Curve Designer Code"+#CRLF$)
        SendMessage_(scintilla, #EM_REPLACESEL, 0, *textBuffer)
        Delay(10)
        FreeMemory(*textBuffer)
      Else
        MessageRequester("Notice:","Scintilla editor not found!"+#CRLF$+#CRLF$ +
                                   "Invoke Curve Designer tool from the PureBasic IDE for this feature.")
      EndIf
    EndIf
  EndProcedure
  
CompilerEndIf

Procedure UpdateTestCanvas()
  StartVectorDrawing(CanvasVectorOutput(testcanvas))
  VectorSourceColor(RGBA(255,255,255,255))
  FillVectorOutput()
  ForEach displaycurves()
    With displaycurves()
      If \moverequired
        MovePathCursor(\startpoint\x, \startpoint\y)
      EndIf
      AddPathCurve(\firstpoint\x,\firstpoint\y,\secondpoint\x,\secondpoint\y,\endpoint\x,\endpoint\y)
    EndWith
  Next
  VectorSourceColor(RGBA(0,0,0,255))
  StrokePath(3,#PB_Path_RoundCorner|#PB_Path_RoundEnd)
  StopVectorDrawing()
EndProcedure

Procedure UndoHandler()
  If curveactive Or (startpoint\x<>0 And startpoint\y<>0 And endpoint\x=0 And endpoint\y=0)
    ClearStructure(startpoint, pointd)
    ClearStructure(firstpoint, pointd)
    ClearStructure(secondpoint, pointd)
    ClearStructure(endpoint, pointd)
    curveactive=0
    RedrawCurves()
  Else
    If ListSize(curves())
      LastElement(curves())
      If curves()\moverequired
        New()
      Else
        moveinprogress=0
        newcurve=0
        SetGadgetState(option7,1)
      EndIf
      AddElement(trashcurves())
      trashcurves()=curves()
      DeleteElement(curves())
      RedrawCurves()
    EndIf
  EndIf
  curveactive=0
  DisableGadget(button_commit, 1)
  ConvertCurvesToOutput()
  LogCurves()
  UpdateTestCanvas()
  SetActiveGadget(canvas)
  If ListSize(curves())=0
    New()
  EndIf
  ConvertCurvesFromOutput()
  RedrawCurves()
  If ListSize(curves()) >= 2 And *first\startpoint\x <> *last\endpoint\x And *first\startpoint\y <> *last\endpoint\y And *first\startpoint\x <> *last\startpoint\x And *first\startpoint\y <> *last\startpoint\y
    DisableGadget(button_closeCurve, 0)
  Else
    DisableGadget(button_closeCurve, 1)
  EndIf
  ScrollHandler()
EndProcedure

Procedure RedoHandler()
  If ListSize(trashcurves())
    LastElement(trashcurves())
    LastElement(curves())
    AddElement(curves())
    curves()=trashcurves()
    DeleteElement(trashcurves())
    curveactive=0
    DisableGadget(button_commit, 1)
  EndIf
  ConvertCurvesToOutput()
  LogCurves()
  UpdateTestCanvas()
  SetActiveGadget(canvas)
  If ListSize(curves())=0
    New()
  EndIf
  curveactive=0
  newcurve=0
  SetGadgetState(option7,1)
  DisableGadget(button_commit,1)
  ConvertCurvesFromOutput()
  RedrawCurves()
  If ListSize(curves()) >= 2 And *first\startpoint\x <> *last\endpoint\x And *first\startpoint\y <> *last\endpoint\y And *first\startpoint\x <> *last\startpoint\x And *first\startpoint\y <> *last\startpoint\y
    DisableGadget(button_closeCurve, 0)
  Else
    DisableGadget(button_closeCurve, 1)
  EndIf
  ScrollHandler()
EndProcedure

Procedure ClearHandler()
  InitializeCanvas()
  SetGadgetState(gadget_zoom,0)
  If GetGadgetAttribute(gadget_zoom, #PB_Button_Image) = ImageID(imgZoomed)
    SetGadgetAttribute(gadget_zoom, #PB_Button_Image, ImageID(imgZoomNormal))
  EndIf
  zoomlevel=0
  HideGadget(gadget_zoom_viewport, 1)
  SetGadgetAttribute(ccontainer,#PB_ScrollArea_InnerWidth,GadgetWidth(canvas))
  SetGadgetAttribute(ccontainer,#PB_ScrollArea_InnerHeight,GadgetHeight(canvas))
  
  SetActiveGadget(canvas)
  ScrollHandler()
EndProcedure


Procedure InitializeCanvas()
  Protected max_viewport.d,w.d,h.d
  ClearList(curves())
  ClearList(displaycurves())
  ClearList(trashcurves())
  ClearList(codelines())
  ClearGadgetItems(gadget_output)
  ClearStructure(startpoint, pointd)
  ClearStructure(firstpoint, pointd)
  ClearStructure(secondpoint, pointd)
  ClearStructure(endpoint, pointd)
  
  
  StartVectorDrawing(CanvasVectorOutput(testcanvas))
  VectorSourceColor(RGBA(255,255,255,255))
  StopVectorDrawing()
  New()
  SetGadgetState(option1,1)
  HideGadget(gadget_output,1)
  HideGadget(testcanvas,1)
  HideGadget(ccontainer,0)
  SetGadgetState(option3,1)
  SetGadgetState(option6,1)
  w.d = ImageWidth(imgOriginalBackground)
  h.d = ImageHeight(imgOriginalBackground)
  If h>w
    height_zoom.d=max_zoom
    width_zoom.d=w/h*max_zoom
  Else
    width_zoom.d=max_zoom
    height_zoom.d=h/w*max_zoom   
  EndIf
  cxborder(1)=width_zoom*0.2
  cyborder(1)=height_zoom*0.2
  cx(1)=0.6*width_zoom
  cy(1)=0.6*height_zoom 
  outputcx=w
  outputcy=h
  ResizeGadget(testcanvas,316,1,outputcx,outputcy)
  SetGadgetText(gadget_cx, Str(outputcx))
  SetGadgetText(gadget_cy, Str(outputcy))
  max_viewport=WindowHeight(0)-10
  If h>w
    height_viewport.d=max_viewport
    width_viewport.d=w/h*max_viewport
  Else
    width_viewport.d=max_viewport
    height_viewport.d=h/w*max_viewport   
  EndIf
  oldwidth_viewport=width_viewport
  oldheight_viewport=height_viewport
  cxborder(0)=width_viewport*0.2
  cyborder(0)=height_viewport*0.2
  cx(0)=0.6*width_viewport
  cy(0)=0.6*height_viewport
  oldcx=cx(0)
  oldcy=cy(0)
  If IsImage(imgBackgrounds(0)) : FreeImage(imgBackgrounds(0)) : EndIf
  imgBackgrounds(0) = CreateImage(#PB_Any,width_viewport,height_viewport,24,#White)
  StartVectorDrawing(ImageVectorOutput(imgBackgrounds(0)))
  VectorSourceColor(RGBA(255,255,255,255))
  FillVectorOutput()
  MovePathCursor(cxborder(0),cyborder(0))
  DrawVectorImage(ImageID(imgOriginalBackground),80,cx(0),cy(0))
  AddPathBox(cxborder(0),cyborder(0),cx(0),cy(0))
  VectorSourceColor(RGBA(100,100,255,255))
  DashPath(1,4)
  StopVectorDrawing()
  If IsImage(imgBackgrounds(1)) : FreeImage(imgBackgrounds(1)) : EndIf
  imgBackgrounds(1) = CreateImage(#PB_Any,width_zoom,height_zoom,24,#White)
  StartVectorDrawing(ImageVectorOutput(imgBackgrounds(1)))
  MovePathCursor(0,0)
  DrawVectorImage(ImageID(imgBackgrounds(0)),255,ImageWidth(imgBackgrounds(1)),ImageHeight(imgBackgrounds(1)))
  StopVectorDrawing()
  If IsImage(imgLayers(0)) : FreeImage(imgLayers(0)) : EndIf
  imgLayers(0) = CreateImage(#PB_Any, width_viewport,height_viewport,32,#PB_Image_Transparent)
  If IsImage(imgLayers(1)) : FreeImage(imgLayers(1)) : EndIf
  imgLayers(1) = CreateImage(#PB_Any,ImageWidth(imgBackgrounds(1)),ImageHeight(imgBackgrounds(1)),32,#PB_Image_Transparent)
  ResizeGadget(canvas, #PB_Ignore,#PB_Ignore,width_viewport,height_viewport)
  SetGadgetAttribute(canvas, #PB_Canvas_Image, ImageID(imgBackgrounds(0)))
  SetGadgetAttribute(canvas, #PB_Canvas_CustomCursor, imgCursor)
  
EndProcedure

Procedure ConvertCurvesToOutput()
  ClearList(displaycurves()) 
  ForEach curves()
    AddElement(displaycurves())
    With displaycurves()
      \moverequired  = curves()\moverequired
      \startpoint\x  = (curves()\startpoint\x-cxborder(zoomlevel))/cx(zoomlevel)*outputcx
      \startpoint\y  = (curves()\startpoint\y-cyborder(zoomlevel))/cy(zoomlevel)*outputcy
      \firstpoint\x  = (curves()\firstpoint\x-cxborder(zoomlevel))/cx(zoomlevel)*outputcx
      \firstpoint\y  = (curves()\firstpoint\y-cyborder(zoomlevel))/cy(zoomlevel)*outputcy
      \secondpoint\x = (curves()\secondpoint\x-cxborder(zoomlevel))/cx(zoomlevel)*outputcx
      \secondpoint\y = (curves()\secondpoint\y-cyborder(zoomlevel))/cy(zoomlevel)*outputcy
      \endpoint\x    = (curves()\endpoint\x-cxborder(zoomlevel))/cx(zoomlevel)*outputcx
      \endpoint\y    = (curves()\endpoint\y-cyborder(zoomlevel))/cy(zoomlevel)*outputcy     
    EndWith
  Next
  ClearList(trashdisplaycurves()) 
  ForEach trashcurves()
    AddElement(trashdisplaycurves())
    With trashdisplaycurves()
      \moverequired  = trashcurves()\moverequired
      \startpoint\x  = (trashcurves()\startpoint\x-cxborder(zoomlevel))/cx(zoomlevel)*outputcx
      \startpoint\y  = (trashcurves()\startpoint\y-cyborder(zoomlevel))/cy(zoomlevel)*outputcy
      \firstpoint\x  = (trashcurves()\firstpoint\x-cxborder(zoomlevel))/cx(zoomlevel)*outputcx
      \firstpoint\y  = (trashcurves()\firstpoint\y-cyborder(zoomlevel))/cy(zoomlevel)*outputcy
      \secondpoint\x = (trashcurves()\secondpoint\x-cxborder(zoomlevel))/cx(zoomlevel)*outputcx
      \secondpoint\y = (trashcurves()\secondpoint\y-cyborder(zoomlevel))/cy(zoomlevel)*outputcy
      \endpoint\x    = (trashcurves()\endpoint\x-cxborder(zoomlevel))/cx(zoomlevel)*outputcx
      \endpoint\y    = (trashcurves()\endpoint\y-cyborder(zoomlevel))/cy(zoomlevel)*outputcy     
    EndWith
  Next
EndProcedure

Procedure ConvertCurvesFromOutput()
  ClearList(curves())
  ForEach displaycurves()
    AddElement(curves())
    With curves()
      \moverequired  = displaycurves()\moverequired
      \startpoint\x  = displaycurves()\startpoint\x*cx(zoomlevel)/outputcx+cxborder(zoomlevel)
      \startpoint\y  = displaycurves()\startpoint\y*cy(zoomlevel)/outputcy+cyborder(zoomlevel)
      \firstpoint\x  = displaycurves()\firstpoint\x*cx(zoomlevel)/outputcx+cxborder(zoomlevel)
      \firstpoint\y  = displaycurves()\firstpoint\y*cy(zoomlevel)/outputcy+cyborder(zoomlevel)
      \secondpoint\x = displaycurves()\secondpoint\x*cx(zoomlevel)/outputcx+cxborder(zoomlevel)
      \secondpoint\y = displaycurves()\secondpoint\y*cy(zoomlevel)/outputcy+cyborder(zoomlevel)
      \endpoint\x    = displaycurves()\endpoint\x*cx(zoomlevel)/outputcx+cxborder(zoomlevel)
      \endpoint\y    = displaycurves()\endpoint\y*cy(zoomlevel)/outputcy+cyborder(zoomlevel)   
    EndWith
  Next
  ClearList(trashcurves())
  ForEach trashdisplaycurves()
    AddElement(trashcurves())
    With trashcurves()
      \moverequired  = trashdisplaycurves()\moverequired
      \startpoint\x  = trashdisplaycurves()\startpoint\x*cx(zoomlevel)/outputcx+cxborder(zoomlevel)
      \startpoint\y  = trashdisplaycurves()\startpoint\y*cy(zoomlevel)/outputcy+cyborder(zoomlevel)
      \firstpoint\x  = trashdisplaycurves()\firstpoint\x*cx(zoomlevel)/outputcx+cxborder(zoomlevel)
      \firstpoint\y  = trashdisplaycurves()\firstpoint\y*cy(zoomlevel)/outputcy+cyborder(zoomlevel)
      \secondpoint\x = trashdisplaycurves()\secondpoint\x*cx(zoomlevel)/outputcx+cxborder(zoomlevel)
      \secondpoint\y = trashdisplaycurves()\secondpoint\y*cy(zoomlevel)/outputcy+cyborder(zoomlevel)
      \endpoint\x    = trashdisplaycurves()\endpoint\x*cx(zoomlevel)/outputcx+cxborder(zoomlevel)
      \endpoint\y    = trashdisplaycurves()\endpoint\y*cy(zoomlevel)/outputcy+cyborder(zoomlevel)   
    EndWith
  Next
EndProcedure

Procedure ResizeHandler()
  Protected.d w,h,max_viewport
  
  ConvertCurvesToOutput()
  w=ImageWidth(imgOriginalBackground)
  h=ImageHeight(imgOriginalBackground)
  max_viewport=WindowHeight(0)-10
  If h>w
    height_viewport.d=max_viewport
    width_viewport.d=w/h*max_viewport
  Else
    width_viewport.d=max_viewport
    height_viewport.d=h/w*max_viewport   
  EndIf
  cxborder(0)=width_viewport*0.2
  cyborder(0)=height_viewport*0.2
  cx(0)=0.6*width_viewport
  cy(0)=0.6*height_viewport
  
  If zoomlevel=0 
    If curveactive
      startpoint\x  = (oldstartpoint\x/oldwidth_viewport)*width_viewport
      startpoint\y  = (oldstartpoint\y/oldheight_viewport)*height_viewport
      endpoint\x    = (oldendpoint\x/oldwidth_viewport)*width_viewport
      endpoint\y    = (oldendpoint\y/oldheight_viewport)*height_viewport
      firstpoint\x  = (oldfirstpoint\x/oldwidth_viewport)*width_viewport
      firstpoint\y  = (oldfirstpoint\y/oldheight_viewport)*height_viewport
      secondpoint\x = (oldsecondpoint\x/oldwidth_viewport)*width_viewport
      secondpoint\y = (oldsecondpoint\y/oldheight_viewport)*height_viewport
    EndIf
  EndIf
  
  If IsImage(imgBackgrounds(0)) : FreeImage(imgBackgrounds(0)) : EndIf
  imgBackgrounds(0) = CreateImage(#PB_Any,width_viewport,height_viewport,24,#White)
  StartVectorDrawing(ImageVectorOutput(imgBackgrounds(0)))
  VectorSourceColor(RGBA(255,255,255,255))
  FillVectorOutput()
  MovePathCursor(cxborder(0),cyborder(0))
  DrawVectorImage(ImageID(imgOriginalBackground),80,cx(0),cy(0))
  AddPathBox(cxborder(0),cyborder(0),cx(0),cy(0))
  VectorSourceColor(RGBA(100,100,255,255))
  DashPath(1,4)
  StopVectorDrawing()
  ResizeGadget(canvas, 1,1,ImageWidth(imgBackgrounds(zoomlevel)),ImageHeight(imgBackgrounds(zoomlevel)))
  SetGadgetAttribute(canvas,#PB_Canvas_Image,ImageID(imgBackgrounds(zoomlevel))) 
  ResizeGadget(ccontainer, 316,1,width_viewport+2,height_viewport+2)
  SetGadgetAttribute(ccontainer, #PB_ScrollArea_InnerWidth, ImageWidth(imgBackgrounds(zoomlevel)))
  SetGadgetAttribute(ccontainer, #PB_ScrollArea_InnerHeight, ImageHeight(imgBackgrounds(zoomlevel)))
  If IsImage(imgLayers(0)) : FreeImage(imgLayers(0)) : EndIf
  imgLayers(0) = CreateImage(#PB_Any, width_viewport,height_viewport,32,#PB_Image_Transparent)
  
  If curveactive
    oldstartpoint=startpoint
    oldfirstpoint=firstpoint
    oldsecondpoint=secondpoint
    oldendpoint=endpoint
  EndIf   
  oldwidth_viewport=width_viewport
  oldheight_viewport=height_viewport
  oldcx=cx(0)
  oldcy=cy(0)
  SetActiveGadget(canvas)
  ConvertCurvesFromOutput()
  RedrawCurves()
  
EndProcedure

Procedure CopyHandler()
  Protected.s code
  code = "; Code inserted by Vector Curve Designer"+#CRLF$ +
         GetGadgetText(gadget_output)+#CRLF$               +
         "; End of Vector Curve Designer code"
  If GetGadgetText(gadget_output)<>""
    SetClipboardText(code+#CRLF$)
    MessageRequester("Notice:", "Code is on the clipboard")
  Else
    MessageRequester("Notice:", "Nothing to copy!")
  EndIf
  SetActiveGadget(canvas)
  
EndProcedure

Procedure LogCurves() ; Build list of codeline strings from Curves() list and display them
  Protected currentcodestring.s,saved_zoomlevel.i
  Protected.s s1,s2,p1,p2,p3,p4,p5,p6
  ConvertCurvesToOutput()
  saved_zoomlevel=zoomlevel
  zoomlevel=0
  ConvertCurvesFromOutput()
  zoomlevel=saved_zoomlevel
  ClearList(codelines())
  ForEach curves()
    If GetGadgetState(option1)
      If curves()\moverequired
        s1.s=StrD((curves()\startpoint\x-cxborder(0))/cx(0)*outputcx,2)
        s2.s=StrD((curves()\startpoint\y-cyborder(0))/cy(0)*outputcy,2)
        currentcodestring="MovePathCursor("+s1+","+s2+")"+#CRLF$
      Else
        currentcodestring=""
      EndIf
      p1.s=StrD((curves()\firstpoint\x-cxborder(0))/cx(0)*outputcx,2)+","
      p2.s=StrD((curves()\firstpoint\y-cyborder(0))/cy(0)*outputcy,2)+","
      p3.s=StrD((curves()\secondpoint\x-cxborder(0))/cx(0)*outputcx,2)+","
      p4.s=StrD((curves()\secondpoint\y-cyborder(0))/cy(0)*outputcy,2)+","
      p5.s=StrD((curves()\endpoint\x-cxborder(0))/cx(0)*outputcx,2)+","
      p6.s=StrD((curves()\endpoint\y-cyborder(0))/cy(0)*outputcy,2)
      currentcodestring+"AddPathCurve("+p1+p2+p3+p4+p5+p6+")"
      AddElement(codelines())
      codelines()=currentcodestring
    Else
      If ListSize(codelines())=0
        currentcodestring="cx.d="+Str(outputcx)+" : "+"cy.d="+Str(outputcy)+#CRLF$
      Else
        currentcodestring=""
      EndIf
      s1.s=StrD(Round((curves()\startpoint\x-cxborder(0))/cx(0)*outputcx,#PB_Round_Nearest)/outputcx,4)+"*cx"
      s2.s=StrD(Round((curves()\startpoint\y-cyborder(0))/cy(0)*outputcy,#PB_Round_Nearest)/outputcy,4)+"*cy"
      If curves()\moverequired
        currentcodestring+"MovePathCursor("+s1+","+s2+")"+#CRLF$
      EndIf
      p1.s=StrD(Round((curves()\firstpoint\x-cxborder(0))/cx(0)*outputcx,#PB_Round_Nearest)/outputcx,4)+"*cx"+","
      p2.s=StrD(Round((curves()\firstpoint\y-cyborder(0))/cy(0)*outputcy,#PB_Round_Nearest)/outputcy,4)+"*cy"+","
      p3.s=StrD(Round((curves()\secondpoint\x-cxborder(0))/cx(0)*outputcx,#PB_Round_Nearest)/outputcx,4)+"*cx"+","
      p4.s=StrD(Round((curves()\secondpoint\y-cyborder(0))/cy(0)*outputcy,#PB_Round_Nearest)/outputcy,4)+"*cy"+","
      p5.s=StrD(Round((curves()\endpoint\x-cxborder(0))/cx(0)*outputcx,#PB_Round_Nearest)/outputcx,4)+"*cx"+","
      p6.s=StrD(Round((curves()\endpoint\y-cyborder(0))/cy(0)*outputcy,#PB_Round_Nearest)/outputcy,4)+"*cy"
      currentcodestring+"AddPathCurve("+p1+p2+p3+p4+p5+p6+")"
      AddElement(codelines())
      codelines()=currentcodestring
    EndIf
  Next
  ClearGadgetItems(gadget_output)
  ForEach codelines()
    AddGadgetItem(gadget_output, -1, codelines())
  Next
EndProcedure

Procedure CommitHandler()
  ClearList(trashcurves())
  SetGadgetState(option7, 1)
  DisableGadget(button_commit, 1)
  If Not newcurve
    LastElement(curves())
    curves()\endpoint\x=startpoint\x
    curves()\endpoint\y=startpoint\y
  EndIf
  AddElement(curves())
  With curves()
    If newcurve
      \moverequired=1
    Else
      \moverequired=0
    EndIf
    \startpoint\x=startpoint\x
    \startpoint\y=startpoint\y
    \firstpoint\x=firstpoint\x
    \firstpoint\y=firstpoint\y
    \secondpoint\x=secondpoint\x
    \secondpoint\y=secondpoint\y
    \endpoint\x=endpoint\x
    \endpoint\y=endpoint\y
  EndWith
  ConvertCurvesToOutput()
  LogCurves()
  startpoint\x=0:startpoint\y=0:endpoint\x=0:endpoint\y=0
  ClearStructure(firstpoint,pointd)
  ClearStructure(secondpoint,pointd)
  curveactive=0
  ConvertCurvesFromOutput()
  RedrawCurves()
  newcurve=0
  SetActiveGadget(canvas)
  *last = LastElement(curves())
  *first = FirstElement(curves())
  If ListSize(curves()) >= 2 And *first\startpoint\x <> *last\endpoint\x And *first\startpoint\y <> *last\endpoint\y And *first\startpoint\x <> *last\startpoint\x And *first\startpoint\y <> *last\startpoint\y
    DisableGadget(button_closeCurve, 0)
  EndIf
  ScrollHandler()
EndProcedure


Procedure CloseCurveHandler()
  DisableGadget(button_closeCurve, 1)
  DisableGadget(button_commit, 0)
  If ListSize(curves())
    LastElement(curves())
    startpoint\x=curves()\endpoint\x
    startpoint\y=curves()\endpoint\y
    
    FirstElement(curves())
    endpoint\x=curves()\startpoint\x
    endpoint\y=curves()\startpoint\y
  EndIf
  With firstpoint
    \x=startpoint\x + (endpoint\x-startpoint\x)/3
    \y=startpoint\y + (endpoint\y-startpoint\y)/3
  EndWith
  With secondpoint
    \x=startpoint\x + ((endpoint\x-startpoint\x)/3)*2
    \y=startpoint\y + ((endpoint\y-startpoint\y)/3)*2
  EndWith
  oldstartpoint\x=startpoint\x
  oldstartpoint\y=startpoint\y
  oldfirstpoint=firstpoint
  oldsecondpoint=secondpoint
  oldendpoint\x=endpoint\x
  oldendpoint\y=endpoint\y
  curveactive=1
  RedrawCurves()
  ScrollHandler()
EndProcedure

Procedure New()
  curveactive=0
  newcurve=1
  DisableGadget(button_commit, 1)
  DisableGadget(button_closeCurve, 1)
  SetGadgetState(option6,1)
EndProcedure

Procedure StringHandler()
  outputcx = Val(GetGadgetText(gadget_cx))
  outputcy = Val(GetGadgetText(gadget_cy))
  SetActiveGadget(canvas)
  
EndProcedure

Procedure OptionHandler()
  Select EventGadget()
    Case option1
      DisableGadget(gadget_cx, 0)
      DisableGadget(gadget_cy, 0)
      DisableGadget(label_cx, 0)
      DisableGadget(label_cy, 0)
      LogCurves()
    Case option2
      DisableGadget(gadget_cx, 1)
      DisableGadget(gadget_cy, 1)
      DisableGadget(label_cx, 1)
      DisableGadget(label_cy, 1)
      LogCurves()
    Case option3
      HideGadget(ccontainer, 0)
      HideGadget(gadget_output, 1)
      HideGadget(testcanvas,1)
    Case option4
      HideGadget(gadget_output, 0)
      HideGadget(ccontainer, 1)
      HideGadget(testcanvas,1)
    Case option5
      UpdateTestCanvas()
      HideGadget(testcanvas,0)
      HideGadget(gadget_output, 1)
      HideGadget(ccontainer, 1)
    Case option6
      New()
    Case option7
      If ListSize(curves())=0
        MessageRequester("Notice:", "No curves to continue!")
        SetGadgetState(option6, 1)
      Else
        newcurve=0
      EndIf
  EndSelect
  SetActiveGadget(canvas)
  
EndProcedure

Procedure RedrawCurves()
  StartDrawing(ImageOutput(imgLayers(zoomlevel)))
  DrawingMode(#PB_2DDrawing_AllChannels)
  Box(0,0,ImageWidth(imgLayers(zoomlevel)),ImageHeight(imgLayers(zoomlevel)),RGBA(0,0,0,0))
  StopDrawing()
  StartVectorDrawing(ImageVectorOutput(imgLayers(zoomlevel)))
  ForEach curves()
    With curves()
      MovePathCursor(\startpoint\x,\startpoint\y)
      AddPathCurve(\firstpoint\x,\firstpoint\y,\secondpoint\x,\secondpoint\y,\endpoint\x,\endpoint\y)
      VectorSourceColor(RGBA(255,0,0,255))
      If zoomlevel
        StrokePath(6,#PB_Path_RoundCorner|#PB_Path_RoundEnd)
      Else
        StrokePath(2)
      EndIf
    EndWith
  Next
  If curveactive
    MovePathCursor(startpoint\x,startpoint\y)
    AddPathCurve(firstpoint\x,firstpoint\y,secondpoint\x,secondpoint\y,endpoint\x,endpoint\y)
    VectorSourceColor(RGBA(255,0,0,255))
    DashPath(1,2)
    AddPathCircle(startpoint\x,startpoint\y,4)
    VectorSourceColor(RGBA(250,0,0,255))
    FillPath()
    AddPathCircle(firstpoint\x,firstpoint\y,4)
    VectorSourceColor(RGBA(248,0,0,255))
    FillPath()         
    AddPathCircle(secondpoint\x,secondpoint\y,4)
    VectorSourceColor(RGBA(246,0,0,255))
    FillPath()         
    AddPathCircle(endpoint\x,endpoint\y,4)
    VectorSourceColor(RGBA(244,0,0,255))
    FillPath()         
  EndIf
  StopVectorDrawing()
  UpdateCanvas()
EndProcedure

Procedure ZoomHandler()
  Protected.i imgZoomViewportOutput
  Protected.d cxsmall,cysmall,x,y
  ConvertCurvesToOutput()
  If GetGadgetAttribute(gadget_zoom, #PB_Button_Image) = ImageID(imgZoomed)
    SetGadgetAttribute(gadget_zoom, #PB_Button_Image, ImageID(imgZoomNormal))
    zoomlevel=0
  Else
    SetGadgetAttribute(gadget_zoom, #PB_Button_Image, ImageID(imgZoomed))
    zoomlevel=1
  EndIf
  Select zoomlevel
    Case 0
      ResizeGadget(canvas,#PB_Ignore,#PB_Ignore, width_viewport, height_viewport)
    Case 1
      ResizeGadget(canvas,#PB_Ignore,#PB_Ignore, ImageWidth(imgBackgrounds(1)),ImageHeight(imgBackgrounds(1)))
  EndSelect
  SetGadgetAttribute(ccontainer,#PB_ScrollArea_InnerWidth,GadgetWidth(canvas))
  SetGadgetAttribute(ccontainer,#PB_ScrollArea_InnerHeight,GadgetHeight(canvas))
  SetGadgetAttribute(canvas, #PB_Canvas_Image, ImageID(imgBackgrounds(zoomlevel)))
  SetGadgetAttribute(canvas, #PB_Canvas_CustomCursor, imgCursor)
  
  
  
  If IsImage(imgZoomViewport):FreeImage(imgZoomViewport):EndIf
  imgZoomViewport=CreateImage(#PB_Any, ImageWidth(imgBackgrounds(0))*0.2,ImageHeight(imgBackgrounds(0))*0.2)
  StartVectorDrawing(ImageVectorOutput(imgZoomViewport))
  MovePathCursor(0,0)
  DrawVectorImage(ImageID(imgBackgrounds(0)),255,ImageWidth(imgZoomViewport),ImageHeight(imgZoomViewport))
  StopVectorDrawing()
  ResizeGadget(gadget_zoom_viewport,#PB_Ignore,#PB_Ignore,ImageWidth(imgZoomViewport),ImageHeight(imgZoomViewport))
  SetGadgetState(gadget_zoom_viewport, ImageID(imgZoomViewport))
  RedrawCurves()
  cxsmall.d = ImageWidth(imgZoomViewport)
  cysmall.d = ImageHeight(imgZoomViewport)
  x.d = GetGadgetAttribute(ccontainer, #PB_ScrollArea_X)
  y.d = GetGadgetAttribute(ccontainer, #PB_ScrollArea_Y)
  imgZoomViewportOutput = CopyImage(imgZoomViewport, #PB_Any)
  StartVectorDrawing(ImageVectorOutput(imgZoomViewportOutput))
  MovePathCursor(0,0)
  DrawVectorImage(ImageID(imgLayers(1)),255,cxsmall,cysmall)   
  AddPathBox(x/ImageWidth(imgBackgrounds(1))*cxsmall+1,
             y/ImageHeight(imgBackgrounds(1))*cysmall+1,
             width_viewport/ImageWidth(imgBackgrounds(1))*cxsmall,
             height_viewport/ImageHeight(imgBackgrounds(1))*cysmall)
  VectorSourceColor(RGBA(255,0,0,255))
  StrokePath(1)
  StopVectorDrawing()
  SetGadgetState(gadget_zoom_viewport, ImageID(imgZoomViewportOutput))
  
  If zoomlevel
    HideGadget(gadget_zoom_viewport, 0)
    If curveactive
      startpoint\x  = startpoint\x/ImageWidth(imgBackgrounds(0))*ImageWidth(imgBackgrounds(1))
      startpoint\y  = startpoint\y/ImageHeight(imgBackgrounds(0))*ImageHeight(imgBackgrounds(1))
      firstpoint\x  = firstpoint\x/ImageWidth(imgBackgrounds(0))*ImageWidth(imgBackgrounds(1))
      firstpoint\y  = firstpoint\y/ImageHeight(imgBackgrounds(0))*ImageHeight(imgBackgrounds(1))
      secondpoint\x = secondpoint\x/ImageWidth(imgBackgrounds(0))*ImageWidth(imgBackgrounds(1))
      secondpoint\y = secondpoint\y/ImageHeight(imgBackgrounds(0))*ImageHeight(imgBackgrounds(1))
      endpoint\x    = endpoint\x/ImageWidth(imgBackgrounds(0))*ImageWidth(imgBackgrounds(1))
      endpoint\y    = endpoint\y/ImageHeight(imgBackgrounds(0))*ImageHeight(imgBackgrounds(1))
      oldstartpoint=startpoint
      oldfirstpoint=firstpoint
      oldsecondpoint=secondpoint
      oldendpoint=endpoint
    EndIf
  Else
    HideGadget(gadget_zoom_viewport, 1)
    If curveactive
      startpoint\x  = startpoint\x/ImageWidth(imgBackgrounds(1))*ImageWidth(imgBackgrounds(0))
      startpoint\y  = startpoint\y/ImageHeight(imgBackgrounds(1))*ImageHeight(imgBackgrounds(0))
      firstpoint\x  = firstpoint\x/ImageWidth(imgBackgrounds(1))*ImageWidth(imgBackgrounds(0))
      firstpoint\y  = firstpoint\y/ImageHeight(imgBackgrounds(1))*ImageHeight(imgBackgrounds(0))
      secondpoint\x = secondpoint\x/ImageWidth(imgBackgrounds(1))*ImageWidth(imgBackgrounds(0))
      secondpoint\y = secondpoint\y/ImageHeight(imgBackgrounds(1))*ImageHeight(imgBackgrounds(0))
      endpoint\x    = endpoint\x/ImageWidth(imgBackgrounds(1))*ImageWidth(imgBackgrounds(0))
      endpoint\y    = endpoint\y/ImageHeight(imgBackgrounds(1))*ImageHeight(imgBackgrounds(0))
      oldstartpoint=startpoint
      oldfirstpoint=firstpoint
      oldsecondpoint=secondpoint
      oldendpoint=endpoint
    EndIf
  EndIf
  
  ConvertCurvesFromOutput()
  RedrawCurves()
  ScrollHandler()
EndProcedure

Procedure CanvasHandler()
  Protected.i cursorx,cursory
  
  Select EventType()
      
    Case #PB_EventType_LeftButtonDown
      If IsImage(imgLayers(zoomlevel)) And IsImage(imgBackgrounds(zoomlevel))
        If Not curveactive
          If startpoint\x=0 And startpoint\y=0
            If newcurve
              startpoint\x = GetGadgetAttribute(canvas, #PB_Canvas_MouseX)
              startpoint\y = GetGadgetAttribute(canvas, #PB_Canvas_MouseY)
              oldstartpoint\x=startpoint\x
              oldstartpoint\y=startpoint\y
              StartVectorDrawing(ImageVectorOutput(imgLayers(zoomlevel)))
              VectorSourceColor(RGBA(250,0,0,255))
              AddPathCircle(startpoint\x,startpoint\y,4)
              FillPath()
              StopVectorDrawing()
              UpdateCanvas()
            Else
              If ListSize(curves())
                LastElement(curves())
                startpoint\x=curves()\endpoint\x
                startpoint\y=curves()\endpoint\y
                oldstartpoint\x=startpoint\x
                oldstartpoint\y=startpoint\y
              EndIf
              endpoint\x = GetGadgetAttribute(canvas, #PB_Canvas_MouseX)
              endpoint\y = GetGadgetAttribute(canvas, #PB_Canvas_MouseY)
              oldendpoint\x=endpoint\x
              oldendpoint\y=endpoint\y
              With firstpoint
                \x=startpoint\x + (endpoint\x-startpoint\x)/3
                \y=startpoint\y + (endpoint\y-startpoint\y)/3
              EndWith
              With secondpoint
                \x=startpoint\x + ((endpoint\x-startpoint\x)/3)*2
                \y=startpoint\y + ((endpoint\y-startpoint\y)/3)*2
              EndWith
              oldfirstpoint=firstpoint
              oldsecondpoint=secondpoint
              curveactive=1
              RedrawCurves()
              DisableGadget(button_commit, 0)
            EndIf
            If ListSize(curves()) >= 2 And *first\startpoint\x <> *last\endpoint\x And *first\startpoint\y <> *last\endpoint\y And *first\startpoint\x <> *last\startpoint\x And *first\startpoint\y <> *last\startpoint\y
              DisableGadget(button_closeCurve, 0)
            EndIf
          ElseIf endpoint\x=0 And endpoint\y=0
            endpoint\x = GetGadgetAttribute(canvas, #PB_Canvas_MouseX)
            endpoint\y = GetGadgetAttribute(canvas, #PB_Canvas_MouseY)
            oldendpoint\x=endpoint\x
            oldendpoint\y=endpoint\y
            With firstpoint
              \x=startpoint\x + (endpoint\x-startpoint\x)/3
              \y=startpoint\y + (endpoint\y-startpoint\y)/3
            EndWith
            With secondpoint
              \x=startpoint\x + ((endpoint\x-startpoint\x)/3)*2
              \y=startpoint\y + ((endpoint\y-startpoint\y)/3)*2
            EndWith
            oldfirstpoint=firstpoint
            oldsecondpoint=secondpoint
            curveactive=1
            RedrawCurves()
            DisableGadget(button_commit, 0)
          EndIf
          If ListSize(curves()) >= 2 And *first\startpoint\x <> *last\endpoint\x And *first\startpoint\y <> *last\endpoint\y And *first\startpoint\x <> *last\startpoint\x And *first\startpoint\y <> *last\startpoint\y
            DisableGadget(button_closeCurve, 0)
          EndIf
        EndIf
      EndIf
      
    Case #PB_EventType_LeftButtonUp
      moveinprogress=0
      on1=0 : on2=0 : on3=0 : on4=0
      ScrollHandler()
      
    Case #PB_EventType_MouseMove
      If IsImage(imgLayers(zoomlevel)) And IsImage(imgBackgrounds(zoomlevel))
        cursorx = GetGadgetAttribute(canvas, #PB_Canvas_MouseX)
        cursory = GetGadgetAttribute(canvas, #PB_Canvas_MouseY)
        If Not moveinprogress And cursorx<width_viewport And cursory<height_viewport
          StartDrawing(ImageOutput(imgLayers(zoomlevel)))
          Select Point(cursorx, cursory)
            Case RGB(250,0,0)
              on1=1 : on2=0 : on3=0 : on4=0
            Case RGB(248,0,0)
              on2=1 : on1=0 : on3=0 : on4=0
            Case RGB(246,0,0)
              on3=1 : on1=0 : on2=0 : on4=0
            Case RGB(244,0,0)
              on4=1 : on1=0 : on2=0 : on3=0
            Default
              on1=0 : on2=0 : on3=0 : on4=0
          EndSelect
          StopDrawing()
        EndIf
        
        If GetGadgetAttribute(EventGadget(),#PB_Canvas_Buttons) & #PB_Canvas_LeftButton
          If curveactive
            moveinprogress=1
            If on1
              startpoint\x=cursorx : startpoint\y=cursory
              oldstartpoint=startpoint
              RedrawCurves()
            ElseIf on2
              firstpoint\x=cursorx : firstpoint\y=cursory
              oldfirstpoint=firstpoint
              RedrawCurves()
            ElseIf on3
              secondpoint\x=cursorx : secondpoint\y=cursory
              oldsecondpoint=secondpoint
              RedrawCurves()
            ElseIf on4
              endpoint\x=cursorx : endpoint\y=cursory
              oldendpoint=endpoint
              RedrawCurves()
            EndIf
          EndIf
        Else
          moveinprogress=0
        EndIf
      EndIf
  EndSelect
  
EndProcedure

Procedure NewImageButtonHandler()
  Protected file$, ext$
  file$ = OpenFileRequester("Select an image","", "Image |*.jpg;*.png;*.bmp",0)
  If file$
    ext$ = GetExtensionPart(file$)
    Select LCase(ext$)
      Case "png"
        UsePNGImageDecoder()
      Case "jpg"
        UseJPEGImageDecoder()
    EndSelect
    FreeImage(imgOriginalBackground)
    imgOriginalBackground = LoadImage(#PB_Any,file$)
    If IsImage(imgOriginalBackground) = 0
      imgOriginalBackground=CreateImage(#PB_Any, max_viewport,max_viewport,24)
    EndIf
  EndIf
  InitializeCanvas()
  SetGadgetState(gadget_zoom,0)
  If GetGadgetAttribute(gadget_zoom, #PB_Button_Image) = ImageID(imgZoomed)
    SetGadgetAttribute(gadget_zoom, #PB_Button_Image, ImageID(imgZoomNormal))
  EndIf
  zoomlevel=0
  HideGadget(gadget_zoom_viewport, 1)
  SetGadgetAttribute(ccontainer,#PB_ScrollArea_InnerWidth,GadgetWidth(canvas))
  SetGadgetAttribute(ccontainer,#PB_ScrollArea_InnerHeight,GadgetHeight(canvas))
  
  SetActiveGadget(canvas)
EndProcedure

Procedure CreateCursor()
  Protected.i color,mask
  CompilerIf #PB_Compiler_OS = #PB_OS_Windows
    Protected icoInf.ICONINFO
    
    color = CreateImage(#PB_Any, 32,32,32,#PB_Image_Transparent)
    
    StartVectorDrawing(ImageVectorOutput(color))
    AddPathCircle(15.5,15.5,2)
    AddPathCircle(15.5,15.5,15)
    MovePathCursor(15.5,13.5)
    AddPathLine(15.5,1.5)
    MovePathCursor(15.5,17.5)
    AddPathLine(15.5,30.5)
    MovePathCursor(13.5,15.5)
    AddPathLine(1.5,15.5)
    MovePathCursor(17.5,15.5)
    AddPathLine(30.5,15.5)
    VectorSourceColor(RGBA(255,0,0,255))
    StrokePath(1)
    StopVectorDrawing()
    
    mask  = CreateImage(#PB_Any, 32,32,32,#PB_Image_Transparent)
    
    StartVectorDrawing(ImageVectorOutput(mask))
    AddPathCircle(15.5,15.5,2)
    AddPathCircle(15.5,15.5,15)
    MovePathCursor(15.5,13.5)
    AddPathLine(15.5,1.5)
    MovePathCursor(15.5,17.5)
    AddPathLine(15.5,30.5)
    MovePathCursor(13.5,15.5)
    AddPathLine(1.5,15.5)
    MovePathCursor(17.5,15.5)
    AddPathLine(30.5,15.5)
    VectorSourceColor(RGBA(255,255,255,255))
    StrokePath(1)
    StopVectorDrawing()
    
    With icoInf.ICONINFO
      \fIcon = #False
      \xHotspot = 15
      \yHotspot = 15
      \hbmMask = ImageID(mask)
      \hbmColor = ImageID(color)
    EndWith
    
    ProcedureReturn CreateIconIndirect(icoInf)
    
  CompilerElseIf  #PB_Compiler_OS = #PB_OS_Linux
    
    Define *cursor.GdkCursor = gdk_cursor_new_from_pixbuf_(gdk_display_get_default_(), ImageID(color), 16, 16)
    
    ProcedureReturn *cursor
    
  CompilerElseIf #PB_Compiler_OS = #PB_OS_MacOS
    
    Define hotSpot.NSPoint
    hotSpot\x = 16
    hotSpot\y = 16
    Define *cursor = CocoaMessage(0, 0, "NSCursor alloc")
    CocoaMessage(0, *cursor, "initWithImage:", ImageID(color), "hotSpot:@", @hotSpot)
    
    ProcedureReturn *cursor   
    
  CompilerEndIf
  
EndProcedure

Procedure UpdateCanvas()
  If IsImage(imgOutput)
    FreeImage(imgOutput)
  EndIf
  imgOutput = CopyImage(imgBackgrounds(zoomlevel),#PB_Any)
  StartVectorDrawing(ImageVectorOutput(imgOutput))
  MovePathCursor(0,0)
  DrawVectorImage(ImageID(imgLayers(zoomlevel)))
  StopVectorDrawing()
  SetGadgetAttribute(canvas, #PB_Canvas_Image, ImageID(imgOutput))
EndProcedure

Last edited by netmaestro on Mon Jan 30, 2023 6:17 pm, edited 56 times in total.
BERESHEIT
RASHAD
PureBasic Expert
PureBasic Expert
Posts: 4622
Joined: Sun Apr 12, 2009 6:27 am

Re: Vector Curve Designer (RAD Tool)

Post by RASHAD »

Hi NM
That is a very serious, very beautiful Tool
Thanks so much
Egypt my love
User avatar
idle
Always Here
Always Here
Posts: 5018
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: Vector Curve Designer (RAD Tool)

Post by idle »

what can I say, that's really useful. :mrgreen:

should work on windows and linux now with this and compilerif around the import

Code: Select all

Procedure CreateCursor()
  
  color = CreateImage(#PB_Any, 32,32,32,#PB_Image_Transparent)
  
  StartVectorDrawing(ImageVectorOutput(color))
  AddPathCircle(15.5,15.5,2)
  AddPathCircle(15.5,15.5,15)
  MovePathCursor(15.5,13.5)
  AddPathLine(15.5,1.5)
  MovePathCursor(15.5,17.5)
  AddPathLine(15.5,30.5)
  MovePathCursor(13.5,15.5)
  AddPathLine(1.5,15.5)
  MovePathCursor(17.5,15.5)
  AddPathLine(30.5,15.5)
  VectorSourceColor(RGBA(255,0,0,255))
  StrokePath(1)
  StopVectorDrawing()
  
  CompilerIf #PB_Compiler_OS = #PB_OS_Windows 
    
    mask  = CreateImage(#PB_Any, 32,32,32,#PB_Image_Transparent)
    
    StartVectorDrawing(ImageVectorOutput(mask))
    AddPathCircle(15.5,15.5,2)
    AddPathCircle(15.5,15.5,15)
    MovePathCursor(15.5,13.5)
    AddPathLine(15.5,1.5)
    MovePathCursor(15.5,17.5)
    AddPathLine(15.5,30.5)
    MovePathCursor(13.5,15.5)
    AddPathLine(1.5,15.5)
    MovePathCursor(17.5,15.5)
    AddPathLine(30.5,15.5)
    VectorSourceColor(RGBA(255,255,255,255))
    StrokePath(1)
    StopVectorDrawing()
    
    With icoInf.ICONINFO
      \fIcon = #False
      \xHotspot = 15
      \yHotspot = 15
      \hbmMask = ImageID(mask)
      \hbmColor = ImageID(color)
    EndWith
    
    ProcedureReturn CreateIconIndirect_(icoInf)
    
  CompilerElseIf  #PB_Compiler_OS = #PB_OS_Linux 
    
    Protected *cursor.GdkCursor = gdk_cursor_new_from_pixbuf_(gdk_display_get_default_(), ImageID(color), 16, 16)
    
    ProcedureReturn *cursor 
    
  CompilerEndIf  
  
EndProcedure
Windows 11, Manjaro, Raspberry Pi OS
Image
User avatar
netmaestro
PureBasic Bullfrog
PureBasic Bullfrog
Posts: 8422
Joined: Wed Jul 06, 2005 5:42 am
Location: Fort Nelson, BC, Canada

Re: Vector Curve Designer (RAD Tool)

Post by netmaestro »

Perfect Idle, thanks! Now just need someone to do MacOS.
BERESHEIT
Little John
Addict
Addict
Posts: 4519
Joined: Thu Jun 07, 2007 3:25 pm
Location: Berlin, Germany

Re: Vector Curve Designer (RAD Tool)

Post by Little John »

The video looks very good, thank you for this useful program.

However, I can't use it myself here. After starting the program, the window is too big on my notebook. I can't see and use the buttons at the bottom of the window. After changing line 61 to

Code: Select all

OpenWindow(0,0,0,1000,700,"")
I can see the whole window, but still can't see the buttons. So making the program window resizable is my first wish. :-)
User avatar
netmaestro
PureBasic Bullfrog
PureBasic Bullfrog
Posts: 8422
Joined: Wed Jul 06, 2005 5:42 am
Location: Fort Nelson, BC, Canada

Re: Vector Curve Designer (RAD Tool)

Post by netmaestro »

Ok I adjusted the size down so it should fit on a 1366*768 screen. It didn't need to be as big as it was anyway.
BERESHEIT
User avatar
Michael Vogel
Addict
Addict
Posts: 2663
Joined: Thu Feb 09, 2006 11:27 pm
Contact:

Re: Vector Curve Designer (RAD Tool)

Post by Michael Vogel »

Nice, only hacked the "New" button to create an empty image by moving some code lines from the loadimage section to a procedure...

Code: Select all

Procedure CheckImage(thisbackground)

	If IsImage(thisbackground)
		w.d = ImageWidth(thisbackground)
		h.d = ImageHeight(thisbackground)
		outputcx=w
		outputcy=h
		SetGadgetText(gadget_cx, Str(outputcx))
		SetGadgetText(gadget_cy, Str(outputcy))
		If h>w
			nh.d=600
			nw.d=w/h*600
		Else
			nw.d=600
			nh.d=h/w*600
		EndIf
		imgBackground = CreateImage(#PB_Any,nw,nh,24,#White)
		StartVectorDrawing(ImageVectorOutput(imgBackground))
		cx=0.6666*nw : cy=0.6666*nh
		MovePathCursor(nw*0.1666,nh*0.1666)
		DrawVectorImage(ImageID(thisbackground),80,cx,cy)
		AddPathBox(nw*0.1666,nh*0.1666,cx,cy)
		VectorSourceColor(RGBA(100,100,255,255))
		DashPath(1,4)
		StopVectorDrawing()
		FreeImage(thisbackground)

		imgLayer = CreateImage(#PB_Any, nw,nh,32,#PB_Image_Transparent)
		FrameGadget(#PB_Any,8,60,nw+4,nh+4,"", #PB_Frame_Flat)
		CanvasGadget(0,10,62,nw,nh,#PB_Canvas_Keyboard|#PB_Canvas_ClipMouse)
		BindGadgetEvent(0, @CanvasHandler())
		SetGadgetAttribute(0, #PB_Canvas_Image, ImageID(imgBackground))
		SetGadgetAttribute(0, #PB_Canvas_CustomCursor, cursor)
		output_font=LoadFont(#PB_Any, "consolas", 11)
		output = EditorGadget(#PB_Any, nw+20, 60, 1250-(nw+20),604)
		SetGadgetFont(output, FontID(output_font))
	EndIf

EndProcedure

Procedure New()

	Protected image

	curveactive=0
	newcurve=1

	If IsImage(imgLayer)=0
		image=CreateImage(#PB_Any,100,100,24,$FFF0F0)
		CheckImage(image)
	EndIf

EndProcedure
wombats
Enthusiast
Enthusiast
Posts: 662
Joined: Thu Dec 29, 2011 5:03 pm

Re: Vector Curve Designer (RAD Tool)

Post by wombats »

Cursor for macOS:

Code: Select all

  CompilerElseIf #PB_Compiler_OS = #PB_OS_MacOS
    
    Define hotSpot.NSPoint
    hotSpot\x = 16
    hotSpot\y = 16
    Define *cursor = CocoaMessage(0, 0, "NSCursor alloc")
    CocoaMessage(0, *cursor, "initWithImage:", ImageID(color), "hotSpot:@", @hotSpot)
    
    ProcedureReturn *cursor
I should point out that ToolbarStandardButton doesn't work on macOS.
Bitblazer
Enthusiast
Enthusiast
Posts: 730
Joined: Mon Apr 10, 2017 6:17 pm
Location: Germany
Contact:

Re: Vector Curve Designer (RAD Tool)

Post by Bitblazer »

Nice. Well done and thanks :)
webpage - discord chat links -> purebasic GPT4All
User avatar
netmaestro
PureBasic Bullfrog
PureBasic Bullfrog
Posts: 8422
Joined: Wed Jul 06, 2005 5:42 am
Location: Fort Nelson, BC, Canada

Re: Vector Curve Designer (RAD Tool)

Post by netmaestro »

@wombats, thanks! Your code is implemented in the first post. I'll probably switch the toolbar for a regular menu anyway.
BERESHEIT
FlatEarth

Re: Vector Curve Designer (RAD Tool)

Post by FlatEarth »

Excellent, thanks for sharing.
User avatar
netmaestro
PureBasic Bullfrog
PureBasic Bullfrog
Posts: 8422
Joined: Wed Jul 06, 2005 5:42 am
Location: Fort Nelson, BC, Canada

Re: Vector Curve Designer (RAD Tool)

Post by netmaestro »

Considerable work has gone into the redesign of this tool, it seems much more intuitive now and it avoids the MacOS ToolbarStandardButtons issue. No more toolbar. Here's a shot of the interface now:

Image

Everything just seems so self-explaining, I hope folks agree.

I guess I have to make a new video now that it looks and works all different.
BERESHEIT
User avatar
Kuron
Addict
Addict
Posts: 1626
Joined: Sat Oct 17, 2009 10:51 pm
Location: Pacific Northwest

Re: Vector Curve Designer (RAD Tool)

Post by Kuron »

netmaestro wrote:Considerable work has gone into the redesign of this tool, it seems much more intuitive now and it avoids the MacOS ToolbarStandardButtons issue.
Looks really nice!
RASHAD
PureBasic Expert
PureBasic Expert
Posts: 4622
Joined: Sun Apr 12, 2009 6:27 am

Re: Vector Curve Designer (RAD Tool)

Post by RASHAD »

Hi NM
- Will you add the next 2 lines to your code to solve PB 5.72 problem with the default font
- Can you redesign the interface to let the working area full for the CanvasGadget() so Window can be 800x600 for exam. and resizable too

Code: Select all

LoadFont(0,"MS Shell Dlg",9 , #PB_Font_HighQuality)
SetGadgetFont(#PB_Default,FontID(0))

OpenWindow(0,0,0,1024,710,"Curve Designer",#PB_Window_ScreenCentered|#PB_Window_SystemMenu)
Egypt my love
User avatar
idle
Always Here
Always Here
Posts: 5018
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: Vector Curve Designer (RAD Tool)

Post by idle »

that's much better now.
Windows 11, Manjaro, Raspberry Pi OS
Image
Post Reply