Vector drawing : rotation and parenting

Just starting out? Need help? Post your questions and find answers here.
User avatar
[blendman]
Enthusiast
Enthusiast
Posts: 297
Joined: Thu Apr 07, 2011 1:14 pm
Location: 3 arks
Contact:

Vector drawing : rotation and parenting

Post by [blendman] »

Hi

I would like to create a "bones system" for animation, in my software Cartoon (Which use the 2D vector drawing).

So, I would like that a shape (box, circle, curve, line...) get some parameters from its "parent" and give some parameters to its children :
- position (x,y)
- rotation
- scale

For Position it's enough simple, and it's ok, even with animation.
But for rotation, I have made some tests, but it doesn't work like I want.

In my code, I would like that the yellow box rotate over the purple box (like the purple rotate over the green box...).
but I don't know how to do that.

I have though to use cos and sin, like with sprite, but I don't know how too.
So any help would be apprecied :)

Thanks


My test :

Code: Select all

; rotation heritage parenting with lib drawing canvas
; by blendman for "Cartoon !" (open-source 2D vector software)
; pb5.73

Structure sColor
  r.a
  g.a
  b.a
  a.a
EndStructure
Structure sParent
  id.W
  Startx.w
  starty.w
EndStructure
Structure sShape
  ; position
  x.d
  y.d
  ; final position with parent heritage
  finalx.d
  finaly.d
  ; size
  w.w
  h.w
  
  ; rotation
  Rot.d
  ; final rotation with parent heritage
  finalRot.d
  ; Final rotation center
  finalRotcx.d
  finalRotcY.d

  ; color
  color.scolor
  ; center of shape
  cx.w
  cy.w
  ; parent shape
  parent.sParent
EndStructure
Global Dim shape.SShape(0)
Global nbShape=-1


Procedure AddShape(x,y,w,h,rot,color,cx,cy,parent=-1)
  nbShape+1
  i=nbshape
  ReDim shape(i)
  With shape(i)
    \x = x 
    \y = y
    \cx = cx
    \cy = cy
    \w = w
    \h = h
    \color\a = Alpha(color)
    \color\r = Red(color)
    \color\g = Green(color)
    \color\b = Blue(color)
    \Rot = rot
    \parent\id = parent
    If parent>-1
      \parent\Startx = shape(parent)\x
      \parent\Starty = shape(parent)\y
    EndIf
  EndWith
  
EndProcedure

Procedure updatecanvas()
  Static rot.d
  Static a
  
  If StartVectorDrawing(CanvasVectorOutput(0))
    
    ; background
    AddPathBox(0,0,GadgetWidth(0),GadgetHeight(0))
    VectorSourceColor(RGBA(255,255,255,255))
    FillPath()
    For i=0 To ArraySize(shape())
      
      
      ; display shapes
      With shape(i)
        ResetCoordinates()
        
        ; to calcul the parent heritage : position (x,y) and rotation (rot)
        
        ; default rotation of parent
        RotParent.d= 0
        ; get the parentID
        p_ID = \parent\id
        ; if shape has a parent, we have to add the position and rotation from this parent
        If p_ID >-1
          
          ; the finalrotation of the parent
          RotParent = shape(p_ID)\finalRot
          
          ; final position
          \finalx = shape(p_ID)\x - \parent\startX + shape(p_ID)\finalx
          \finaly = shape(p_ID)\y - \parent\starty + shape(p_ID)\finaly
          
          ; final center For rotation
          \finalRotcx = shape(p_ID)\x + shape(p_ID)\cx-(\x+\cx+\finalx) + shape(p_ID)\finalRotcx + shape(p_ID)\finalx
          \finalRotcY = shape(p_ID)\y + shape(p_ID)\cy-(\y+\cy+\finaly) + shape(p_ID)\finalRotcy + shape(p_ID)\finaly
          
        EndIf
        
        
        ; default rotation by shape : 
        \finalRot = \rot + RotParent
        
        ; change rotation for some shapes
        If i=0 
          ; we rotate the box_0
          \finalRot = \rot + rot + RotParent
          
          ; animation of the positon X (for the Shape0)
          \x +a
          If a= 0
            a=1
          EndIf
          If \x> 500
            a=-1
          Else
            If \x<=50
              If a<0  
                a=1
              EndIf
            EndIf
          EndIf 
          
        ElseIf i = 1
          ; the box_1 isn't rotated
          \finalRot = \rot + RotParent
        ElseIf i = 2
          ; the box_2 should be rotated
          ; if I do a 2nd rotation, it doesn't work
          \finalRot = \rot +RotParent 
        EndIf
        
        ; then rotate the coordinates
        If \finalRot <> 0
          ; RotateCoordinates(\x+\cx+\finalRotcx,\y+\cy+\finalRotcy,\finalRot)
          RotateCoordinates(\x+\cx+\finalx+\finalRotcx,\y+\cy+\finaly+\finalRotcy,\finalRot)
         
          If i=2
            ; doesn't work
            ; \finalRot + rot
            ; RotateCoordinates(\x+\cx,\y+\cy,\finalRot)
            
            ; doesn't work
            RotateCoordinates(\x+\cx+\finalx,\y+\cy+\finaly, Rot)
          EndIf
          
        EndIf
        
        ; display the shapes
        MovePathCursor(\x,\y)
        AddPathBox(\x+\finalx,\y+\finaly,\w,\h)
        VectorSourceColor(RGBA(\color\r,\color\g,\color\b,\color\a))
        FillPath()
        AddPathCircle(\x+\cx+\finalx,\y+\cy+\finaly,5)
        VectorSourceColor(RGBA(50,0,0,200))
        FillPath()
      EndWith
    Next
    rot+0.2
    StopVectorDrawing()
  EndIf
EndProcedure
w=1024
h=600
If OpenWindow(0, 0, 0, w, h, "VectorDrawing rotation heritage & parenting", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
  CanvasGadget(0, 0, 0, w, h)
  
  x = 450
  y = 300
  AddShape(x,y,100,80,0,RGBA(200,100,50,255),25,25) : x+100
  AddShape(x,y,150,60,0,RGBA(00,100,50,255),25,25,0) : x+100
  AddShape(x,y,120,50,0,RGBA(100,00,150,255),25,25,1): x+100
  AddShape(x,y,100,40,0,RGBA(255,255,0,255),25,25,2)
  updatecanvas()
  Repeat
    
    Repeat
      Event = WaitWindowEvent(1)
    Until event = 0 Or Event = #PB_Event_CloseWindow
    
    updatecanvas()
  Until Event = #PB_Event_CloseWindow
EndIf


#NULL
Addict
Addict
Posts: 1440
Joined: Thu Aug 30, 2007 11:54 pm
Location: right here

Re: Vector drawing : rotation and parenting

Post by #NULL »

Here is a little demo how it could work. Not sure how the object definition might be different from yours, but just something you can experiment with.
You could also replace some MoveCursor() with TranslateCoordinates() i think, that would be more in line with how the rotation is done.

Code: Select all

EnableExplicit
Define ww, wh, style, win, canvas, event, quit

ww=800
wh=600
style | #PB_Window_ScreenCentered
style | #PB_Window_SystemMenu
style | #PB_Window_MinimizeGadget

win = OpenWindow(#PB_Any, 50, 100, ww, wh, "", style)
AddKeyboardShortcut(win, #PB_Shortcut_Escape, 10)
canvas = CanvasGadget(#PB_Any, 0, 0, ww, wh)
AddWindowTimer(win, 1, 10)

Structure object
  p.i
  x.i
  y.i
  w.i
  h.i
  cx.i
  cy.i
  a.f
EndStructure

Dim o.object(2)
With o(0)
  \p= -1 : \x = 200 : \y = 200 : \w = 100 : \h = 10 : \cx = 0 : \cy = 0 : \a = 0
EndWith
With o(1)
  ; for convenience in this setup we first set the rotation position relative to the parent,
  ; then how much the rotation position is offset from the actuall position and then the
  ; actual position from that..
  \p=  0 : \x = 100-5 : \y = 5 : \w = 80 : \h = 40 : \cx = 20 : \cy = 20 : \a = 0
  \x - \cx
  \y - \cy
EndWith
With o(2)
  \p=  1 : \x = 80 : \y = 40 : \w = 10 : \h = 100 : \cx = 5 : \cy = 5 : \a = 0
  \x - \cx
  \y - \cy
EndWith

Repeat
  Repeat
    event = WindowEvent()
    Select event
      Case #PB_Event_CloseWindow
        quit = #True
      Case #PB_Event_Menu
        Select EventMenu()
          Case 10
            quit = #True
        EndSelect
      Case #PB_Event_Timer
        Select EventTimer()
          Case 1
            Define anim
            anim = ElapsedMilliseconds() / 2000
            
            If StartVectorDrawing(CanvasVectorOutput(canvas))
              VectorSourceColor($ffffffff)
              FillVectorOutput()
              Define i, currX, currY
              currX = 100
              currY = 100
              For i=0 To ArraySize(o())
                With o(i)
                  If i = anim
                    MovePathCursor(currX, currY)
                    AddPathCircle(0, 0, 2, 0, 360, #PB_Path_Relative)
                    VectorSourceColor($aa0000ff) ; red, parent origin
                    StrokePath(2)
                    
                    MovePathCursor(currX, currY)
                    MovePathCursor(\x, \y, #PB_Path_Relative)
                    AddPathCircle(0, 0, 2, 0, 360, #PB_Path_Relative)
                    VectorSourceColor($aa00ff00) ; green, x/y
                    StrokePath(2)
                    
                    MovePathCursor(currX, currY)
                    MovePathCursor(\x + \cx, \y + \cy, #PB_Path_Relative)
                    AddPathCircle(0, 0, 2, 0, 360, #PB_Path_Relative)
                    VectorSourceColor($aaff0000) ; blue, x/y + cx/cy
                    StrokePath(2)
                  EndIf
                  
                  MovePathCursor(currX, currY)
                  MovePathCursor(\x + \cx, \y + \cy, #PB_Path_Relative)
                  RotateCoordinates(0, 0, \a)
                  MovePathCursor(-\cx, -\cy, #PB_Path_Relative)
                  AddPathBox(0, 0, \w, \h, #PB_Path_Relative)
                  currX = PathCursorX()
                  currY = PathCursorY()
                  Define gray
                  gray = 100+ i*50
                  VectorSourceColor(RGBA(gray, gray, gray, $aa))
                  StrokePath(2)
                  
                  If anim > ArraySize(o())
                    \a + (0.1 * (i+1))
                  ElseIf i = anim
                    Break
                  EndIf
                EndWith
              Next
              StopVectorDrawing()
            EndIf
        EndSelect
    EndSelect
  Until Not event
Until quit
#NULL
Addict
Addict
Posts: 1440
Joined: Thu Aug 30, 2007 11:54 pm
Location: right here

Re: Vector drawing : rotation and parenting

Post by #NULL »

When doing the rotation, the trick is to move to the center of rotation first, then rotate the coordinate system, and then move backwards as per the rotation center position to get the rotated origin and then draw the box.
I used a similar trick in the object setup where i basically set the rotation position first and then calculate the origin position from that (or something to that effect).
#NULL
Addict
Addict
Posts: 1440
Joined: Thu Aug 30, 2007 11:54 pm
Location: right here

Re: Vector drawing : rotation and parenting

Post by #NULL »

Well, when I think about it, for a bone system it is probably more intuitive use the x/y relative-to-parent position as an anchor, that already is the center of rotation, and only move backward by the cx/cy from there to get box drawing position. For this you would leave out the x - cx (and y - cy) part in the setup and then instead of

Code: Select all

MovePathCursor(\x + \cx, \y + \cy, #PB_Path_Relative)
use just

Code: Select all

MovePathCursor(\x, \y, #PB_Path_Relative)
and the backward move stays the same. I don't know if you already had it thought out like that in your on code.
User avatar
[blendman]
Enthusiast
Enthusiast
Posts: 297
Joined: Thu Apr 07, 2011 1:14 pm
Location: 3 arks
Contact:

Re: Vector drawing : rotation and parenting

Post by [blendman] »

Hi

Thank you very much !!
I have tried your code and it works fine !
Now, I have to try to understand how to adapt it in my code ;)

Thanks very much again.
Post Reply