Another First App - Playing with Wind and Tide

Any sailors out there? Anyone learning to sail, or anyone who might teach sailing, may find this simple Codea script useful / informative in demonstrating the interaction of boat, tide, true wind and apparent wind vectors!

-- ****************************************
-- Experimental Boat Tide and Wind vectors
-- By Tony Southwood
-- Copyright 2014 - All Rights Reserved
-- ****************************************

function setup()
    supportedOrientations(LANDSCAPE_ANY)
    displayMode(STANDARD)
    parameter.integer("boatHeading", 0,359,0)
    parameter.number("boatSpeed",0,10,10)
    parameter.integer("windSpeed",0,20,10)
    parameter.integer("windDirection",0,359,90)
    parameter.integer("tidalSet",0,359,0)
    parameter.number("tidalRate",0,5,0)  
    parameter.integer("scale",1,25,20)
end

function draw()
    smooth()
    background(40, 40, 50)
    ox = WIDTH / 2
    oy = HEIGHT / 2        
    bh = boatHeading
    bs = boatSpeed
    ts = tidalSet
    tr = tidalRate
    twn= windDirection
    twd = windDirection
    tws = windSpeed
    twa = 0 
    cd = 0
    cs = 0    
    awn = 0
    awd = 0
    aws = 0
    awa = 0
    
    b = vec2(bs * math.cos(math.rad(bh)), bs * math.sin(math.rad(bh)))
    t = vec2(tr * math.cos(math.rad(ts)), tr * math.sin(math.rad(ts)))
    w = vec2(tws * math.cos(math.rad(twd)), tws * math.sin(math.rad(twd)))
    c = vec2(b.x + t.x, b.y + t.y)
    a = vec2(b.x + t.x + w.x, b.y + t.y + w.y)
    twa = math.abs(math.deg(b:angleBetween(w)))
    awa = math.floor(math.abs(math.deg(b:angleBetween(a))))
    bv = vec2(b.x * scale, b.y * scale)
    bv = bv:rotate(math.rad(90))
    bv.x = -bv.x
    tv = vec2(t.x * scale, t.y * scale)
    tv = tv:rotate(math.rad(90))
    tv.x = -tv.x
    wv = vec2(w.x * scale, w.y * scale)
    wv = wv:rotate(math.rad(90))
    wv.x = -wv.x
    cv = vec2(c.x * scale, c.y * scale)
    cv = cv:rotate(math.rad(90))
    cv.x = -cv.x
    av = vec2(a.x * scale, a.y * scale)
    av = av:rotate(math.rad(90))
    av.x = -av.x
    
    cs = math.sqrt((c.x * c.x) + (c.y * c.y))
    cd = math.floor(math.deg(math.atan2(c.y, c.x)))
    if cd < 0 then
        cd = math.fmod(cd + 360,360)
    end
    
    aws = math.sqrt((a.x * a.x) + (a.y + a.y))
    aws = math.sqrt(math.abs(a.x * a.x) + math.abs(a.y * a.y))
    awd = math.deg(math.atan2(a.y, a.x))
    if awd < 0 then
        awd = math.fmod(awd + 360, 360)
    end
    awn = math.fmod(awd + 180, 360)
    
    output.clear()
    -- Draw boat vector
    fontSize(14)
    strokeWidth(8)
    stroke(255,255,255,255)
    line(ox,oy,ox + bv.x, oy + bv.y)
    -- Draw Tide vector
    strokeWidth(8)
    stroke(42, 47, 203, 255)
    line(ox + bv.x, oy + bv.y, ox + bv.x + tv.x, oy + bv.y + tv.y)
    -- Draw Course made good
    strokeWidth(4)
    stroke(229, 14, 11, 126)
    line(ox,oy, ox + bv.x + tv.x, oy + bv.y + tv.y)
    -- Draw true Wind vector
    strokeWidth(8)
    stroke(68, 180, 34, 255)
    line(ox + bv.x + tv.x, oy + bv.y + tv.y, ox + bv.x + tv.x + wv.x, oy + bv.y + tv.y + wv.y)
    -- Draw Apparent wind vector
    strokeWidth(4)
    stroke(68, 180, 34, 127)
    line(ox, oy, ox + bv.x + tv.x + wv.x, oy + bv.y + tv.y + wv.y)
    
    textMode(CORNER)
    t = "Hdg: " ..bh .." Spd: " ..math.floor(bs * 100) / 100 .."k"
    t = t .." Tide: " ..ts .." x " ..math.floor(tr * 100) / 100 .."k"
    t = t .." CMG: " ..cd .." SMG: " ..math.floor(cs * 100) / 100 .."k"
    t = t .." Wind Bow Angles - True: " ..twa .." x " ..math.floor(tws * 100) / 100 .."k"
    t = t .." App: " ..awa .." x " ..math.floor(aws * 100) / 100 .."k"   
    fill(252, 252, 252, 255)
    txtW = textSize(t)
    text(t, (WIDTH/2) - (txtW/2), 40)
    fontSize(40)
    t = "Playing with Wind and Tide"
    txtW = textSize(t)
    text(t, (WIDTH/2) - (txtW/2), HEIGHT - 50)
    
end       


```

Nice piece of work! =D>

That’s awesome, don’t know what I’d use it for, other than calculating wind and tide when I’m on my boat… :^o

Indeed, a bit ‘niche’ I know but…

In teaching sailing it’s handy to have a visual tool to demonstrate to students how the Apparent Wind changes in gusts or as boat speed changes and show how the apparent wind gives a ‘lift’ with Lee Bow Tide/Current and a ‘header’ with a Windward Bow Tide/Current. And, to reinforce how EVERY shift in Apparent Wind requires a change to Sail settings!

As a first app it was really good to learn about using…
Vectors, Vector Arithmetic
Converting Vectors for plot on ‘Real World’ Compass Rose
(And in updated version included below)
Using Sprites, Translating and Rotating
Implementing Touch Control

-- ****************************************
-- Experimental Boat Tide and Wind vectors
-- By Tony Southwood
-- Copyright 2014 - All Rights Reserved
-- ****************************************

function setup()
    supportedOrientations(LANDSCAPE_ANY)
    displayMode(FULLSCREEN_NO_BUTTONS)
    parameter.integer("boatHeading", 0,359,0)
    parameter.number("boatSpeed",0,10,10)
    parameter.integer("windSpeed",0,20,10)
    parameter.integer("windDirection",0,359,90)
    parameter.integer("tidalSet",0,359,0)
    parameter.number("tidalRate",0,5,0)  
    parameter.integer("scale",1,25,20)
    --myImage = readImage("Dropbox:boat-hull")
    --saveImage("Documents:boat-hull", myImage)
    tMode = 0
end

function touched(touch)
    output.clear()
    if touch.tapCount == 1 and touch.state == MOVING then
        tMode = 1
        boatHeading = math.fmod(boatHeading + touch.deltaX, 360)
        if boatHeading < 0 then 
            boatHeading = boatHeading + 360
        end
        boatSpeed = boatSpeed + touch.deltaY / 5
        if boatSpeed < 0 then
            boatSpeed = 0
        elseif boatSpeed > 10 then
            boatSpeed = 10
        end
    elseif touch.tapCount == 2 and touch.state == MOVING then
        tMode = 2
        windDirection = math.fmod(windDirection + touch.deltaX, 360)
        if windDirection < 0 then
            windDirection = windDirection + 360
        end
        windSpeed = windSpeed + touch.deltaY / 5
        if windSpeed < 0 then
            windSpeed = 0
        elseif windSpeed > 20 then
            windSpeed = 20
        end
    elseif touch.tapCount == 3 and touch.state == MOVING then
        tMode = 3
        tidalSet = math.fmod(tidalSet + touch.deltaX, 360)
        if tidalSet < 0 then
            tidalSet = tidalSet + 360
        end
        tidalRate = tidalRate + touch.deltaY / 5
        if tidalRate < 0 then
            tidalRate = 0
        elseif tidalRate > 5 then
            tidalRate = 5
        end
    else
        tMode = 0
    end
    --if touch.state == MOVING then
    --    print(tMode)
    --    print(touch.deltaX)
    --    print(touch.deltaY)
    --end
end

function draw()
    smooth()
    background(40, 40, 50)
    output.clear()
    ox = WIDTH / 2
    oy = HEIGHT / 2  
    bh = boatHeading
    bs = boatSpeed
    ts = tidalSet
    tr = tidalRate
    twn= windDirection
    twd = windDirection
    tws = windSpeed
    twa = 0 
    cd = 0
    cs = 0    
    awn = 0
    awd = 0
    aws = 0
    awa = 0
    
    b = vec2(bs * math.cos(math.rad(bh)), bs * math.sin(math.rad(bh)))
    t = vec2(tr * math.cos(math.rad(ts)), tr * math.sin(math.rad(ts)))
    w = vec2(tws * math.cos(math.rad(twd)), tws * math.sin(math.rad(twd)))
    c = vec2(b.x + t.x, b.y + t.y)
    a = vec2(b.x + t.x + w.x, b.y + t.y + w.y)
    twa = math.abs(math.deg(b:angleBetween(w)))
    awa = math.floor(math.abs(math.deg(b:angleBetween(a))))
    
    haAngle = math.deg(b:angleBetween(a))
    --print(haAngle)
    -- Negative haAngle is apparent wind on port bow
    -- Positive haAngle is apparent wind on stbd bow
    
    bv = vec2(b.x * scale, b.y * scale)
    bv = bv:rotate(math.rad(90))
    bv.x = -bv.x
    tv = vec2(t.x * scale, t.y * scale)
    tv = tv:rotate(math.rad(90))
    tv.x = -tv.x
    wv = vec2(w.x * scale, w.y * scale)
    wv = wv:rotate(math.rad(90))
    wv.x = -wv.x
    cv = vec2(c.x * scale, c.y * scale)
    cv = cv:rotate(math.rad(90))
    cv.x = -cv.x
    av = vec2(a.x * scale, a.y * scale)
    av = av:rotate(math.rad(90))
    av.x = -av.x
    
    cs = math.sqrt((c.x * c.x) + (c.y * c.y))
    cd = math.floor(math.deg(math.atan2(c.y, c.x)))
    if cd < 0 then
        cd = math.fmod(cd + 360,360)
    end
    
    aws = math.sqrt((a.x * a.x) + (a.y + a.y))
    aws = math.sqrt(math.abs(a.x * a.x) + math.abs(a.y * a.y))
    awd = math.deg(math.atan2(a.y, a.x))
    if awd < 0 then
        awd = math.fmod(awd + 360, 360)
    end
    awn = math.fmod(awd + 180, 360)
    pushMatrix()
    translate(ox,oy)
    rotate(-bh)
    sprite("Space Art:Red Ship", 0,0)
    popMatrix()
    -- Draw boat vector
    fontSize(14)
    strokeWidth(8)
    stroke(255,255,255,255)
    line(ox,oy,ox + bv.x, oy + bv.y)
    -- Draw Tide vector
    strokeWidth(8)
    stroke(42, 47, 203, 255)
    line(ox + bv.x, oy + bv.y, ox + bv.x + tv.x, oy + bv.y + tv.y)
    -- Draw Course made good
    strokeWidth(4)
    stroke(229, 14, 11, 126)
    line(ox,oy, ox + bv.x + tv.x, oy + bv.y + tv.y)
    -- Draw true Wind vector
    strokeWidth(8)
    stroke(68, 180, 34, 255)
    line(ox + bv.x + tv.x, oy + bv.y + tv.y, ox + bv.x + tv.x + wv.x, oy + bv.y + tv.y + wv.y)
    -- Draw Apparent wind vector
    strokeWidth(4)
    stroke(68, 180, 34, 127)
    line(ox, oy, ox + bv.x + tv.x + wv.x, oy + bv.y + tv.y + wv.y)
    
    textMode(CORNER)
    t = "Hdg: " ..bh .." Spd: " ..math.floor(bs * 100) / 100 .."k"
    t = t .." Tide: " ..ts .." x " ..math.floor(tr * 100) / 100 .."k"
    t = t .." CMG: " ..cd .." SMG: " ..math.floor(cs * 100) / 100 .."k"
    t = t .." Wind Bow Angles - True: " ..twa .." x " ..math.floor(tws * 100) / 100 .."k"
    t = t .." App: " ..awa .." x " ..math.floor(aws * 100) / 100 .."k"   
    fill(252, 252, 252, 255)
    txtW = textSize(t)
    text(t, (WIDTH/2) - (txtW/2), 60)
    fontSize(40)
    t = "Playing with Wind and Tide"
    txtW = textSize(t)
    text(t, (WIDTH/2) - (txtW/2), HEIGHT - 50)
    fontSize(12)
    fill(222, 153, 24, 255)
    t = "To adjust Dirn and Speed of... BOAT: Tap, Drag - WIND: Tap, Tap, Drag - TIDE: Tap, Tap, Tap and Drag"
    txtW = textSize(t)
    text(t, (WIDTH / 2) - (txtW/2), 40)
    
    fill(222, 23, 48, 255)
    t = "THREE Finger TRIPLE Tap to restore BUTTONS to Adjust Parameters or EXIT  Programme"
    txtW = textSize(t)
    text(t, (WIDTH / 2) - (txtW/2),20)
   
end       

```