I am writing a program in Swift which requires a method that uses specification of two CGPoints and returns all points in a straight line in-between them. At the moment I am trialling the following method, but it is very glitchy and refuses to get all points for some lines. I was just wondering if there is a more efficient way of doing this?
func addPointsInLine(#start: CGPoint, end: CGPoint){
var speed = 0
var startNo = trackPoints.count
var endNo = startNo
var xDiff = start.x - end.x
var yDiff = start.y - end.y
var xChange = 0.0
var yChange = 0.0
var newPointX = start.x
var newPointY = start.y
var ended = false
xChange = Double(xDiff) / Double(yDiff)
yChange = Double(yDiff) / Double(xDiff)
/*if(xDiff > 0){
xChange = sqrt(xChange * xChange)
}
if(yDiff > 0){
yChange = sqrt(yChange * yChange)
}*/
println("xc \(xChange)")
println("yc \(yChange)")
var y = Double(start.y)
var x = Double(start.x)
while !ended {
println(trackPoints.count)
speed++
endNo++
if(CGPointMake(newPointX, newPointY) == end){
ended = true
}
if(yChange > xChange){
y++
x += xChange
trackPoints.append(TrackPoint(x: Int(x), y: Int(y)))
if(CGFloat(Int(y)) == end.y){
ended = true
println("end")
//break
}
/* if(yChange > 0){
if(CGFloat(Int(y)) > end.y){
ended = true
println("end>y")
// break
}
}
if(yChange < 0){
if(CGFloat(Int(y)) < end.y){
ended = true
println("end<y")
//break
}
}*/
if(xChange > 0){
if(CGFloat(Int(x)) >= end.x){
ended = true
println("end>x")
//break
}
}
if(xChange < 0){
if(CGFloat(Int(x)) <= end.x){
ended = true
println("end<x")
//break
}
}
} else {
x++
y += yChange
trackPoints.append(TrackPoint(x: Int(x), y: Int(y)))
if(CGFloat(Int(x)) == end.x){
ended = true
println("end")
//break
}
/* if(xChange > 0){
if(CGFloat(Int(x)) >= end.x){
ended = true
println("end>x")
//break
}
}
if(xChange < 0){
if(CGFloat(Int(x)) <= end.x){
ended = true
println("end<x")
//break
}
}*/
if(yChange > 0){
if(CGFloat(Int(y)) > end.y){
ended = true
println("end>y")
// break
}
}
if(yChange < 0){
if(CGFloat(Int(y)) < end.y){
ended = true
println("end<y")
//break
}
}
}
}
println("finished")
var i = startNo
println(startNo)
println(endNo)
while i < endNo {
trackPoints[i].speed = speed
i++
}
println("finish2")
}
I think firstly finding all points isn't possible because there are infinite points in a straight line. Take example of line joining (0,0) to (1, 0). All the following points and many more are on the said line (0.00001,0) (0.0000000000001,0) (0.01,0)
So you need to limit the amount of points you need to find like all the points with Integer co-ordinates. All the points 1 unit apart, starting from starting point etc.
Next you can use one of equations of line to get points: Equations of Line
Try this,
func findAllPointsBetweenTwoPoints(startPoint : CGPoint, endPoint : CGPoint) {
var allPoints :[CGPoint] = [CGPoint]()
let deltaX = fabs(endPoint.x - startPoint.x)
let deltaY = fabs(endPoint.y - startPoint.y)
var x = startPoint.x
var y = startPoint.y
var err = deltaX-deltaY
var sx = -0.5
var sy = -0.5
if(startPoint.x<endPoint.x){
sx = 0.5
}
if(startPoint.y<endPoint.y){
sy = 0.5;
}
repeat {
let pointObj = CGPoint(x: x, y: y)
allPoints.append(pointObj)
let e = 2*err
if(e > -deltaY)
{
err -= deltaY
x += CGFloat(sx)
}
if(e < deltaX)
{
err += deltaX
y += CGFloat(sy)
}
} while (round(x) != round(endPoint.x) && round(y) != round(endPoint.y));
allPoints.append(endPoint)
}
Related
I have radar chart with data.I want to give index for slider Based on data count.If i am moving slider the graph value should move.If suppose i am at bellow of middle value slider thumb should go to near index.
for _ in 0..<3 {
let mind = arc4random_uniform(mult) + min
let bre = arc4random_uniform(mult) + min
let rel = arc4random_uniform(mult) + min
dataPoints.append(GraphDataPoints(mindfulness: Double(mind), breathing: Double(bre), relaxation: Double(rel)))
}
self.setChartData(mindfulness: dataPoints[0].mindfulness, breathing: dataPoints[0].breathing, relaxation: dataPoints[0].relaxation)
numberofPointsOnSlider = Float(100 / (dataPoints.count - 1))
numberofPointsOnSlider is percent to move.In lastSliderValue i am storing the slider value.pointIndex have index.
#objc func sliderTouchEvents(slider:UISlider,event: UIEvent){
slider.isContinuous = true
if let touchEvent = event.allTouches?.first{
switch touchEvent.phase{
case .began:
lastSliderValue = slider.value
break
case .moved:
if slider.value.truncatingRemainder(dividingBy: numberofPointsOnSlider) == 0{
if lastSliderValue < slider.value || lastSliderValue > slider.value{
pointIndex = Int(slider.value / numberofPointsOnSlider)
self.isIncrement = false
}
lastSliderValue = slider.value
}
if !animation {
if pointIndex >= 0 && pointIndex < dataPoints.count {
var percentToMove:Float = 0
if lastSliderValue > slider.value && pointIndex > 0 {
percentToMove = (100 * (numberofPointsOnSlider - (slider.value.truncatingRemainder(dividingBy: numberofPointsOnSlider) ))) / numberofPointsOnSlider
mindfulDiff = Float(dataPoints[pointIndex].mindfulness - dataPoints[pointIndex - 1].mindfulness)
relaxationDiff = Float(dataPoints[pointIndex].relaxation - dataPoints[pointIndex - 1].relaxation)
breathingDiff = Float(dataPoints[pointIndex].breathing - dataPoints[pointIndex - 1].breathing)
}else if lastSliderValue < slider.value && pointIndex < dataPoints.count - 1 {
percentToMove = (100 * (slider.value.truncatingRemainder(dividingBy: numberofPointsOnSlider) )) / numberofPointsOnSlider
mindfulDiff = Float(dataPoints[pointIndex].mindfulness - dataPoints[pointIndex + 1].mindfulness)
relaxationDiff = Float(dataPoints[pointIndex].relaxation - dataPoints[pointIndex + 1].relaxation)
breathingDiff = Float(dataPoints[pointIndex].breathing - dataPoints[pointIndex + 1].breathing)
}
mp = Float(dataPoints[pointIndex].mindfulness) - (mindfulDiff / 100 ) * percentToMove
rp = Float(dataPoints[pointIndex].relaxation) - (relaxationDiff / 100 ) * percentToMove
bp = Float(dataPoints[pointIndex].breathing) - (breathingDiff / 100) * percentToMove
UIView.animate(withDuration: 10, animations: {
self.setChartData(mindfulness: Double(self.mp) , breathing: Double(self.bp) , relaxation: Double(self.rp) )
})
}
else {
if (pointIndex < 0) {
pointIndex = 0
}
}
}
break
case .ended:
let progDiff = lastSliderValue - slider.value
print("End movie, \(pointIndex) , \(slider.value) ,progDiff: \(progDiff)")
if progDiff < 0{
if (progDiff * -1) >= numberofPointsOnSlider / 2 {
animation = true
UIView.animate(withDuration: 10, animations: {
slider.value += self.numberofPointsOnSlider + progDiff
})
pointIndex = Int(slider.value / numberofPointsOnSlider)+1
// if isIncrement {
// pointIndex += pointIndex
// }else {
// isIncrement = true
//
// }
mindfulDiff = Float(dataPoints[pointIndex].mindfulness - Double(mp))
relaxationDiff = Float(dataPoints[pointIndex].relaxation - Double(rp))
breathingDiff = Float(dataPoints[pointIndex].breathing - Double(bp))
doGraphAnimate()
}else {
animation = true
UIView.animate(withDuration: 10, animations: {
slider.value = self.lastSliderValue
})
mindfulDiff = Float(dataPoints[pointIndex].mindfulness - Double(mp))
relaxationDiff = Float(dataPoints[pointIndex].relaxation - Double(rp))
breathingDiff = Float(dataPoints[pointIndex].breathing - Double(bp))
doGraphAnimate()
}
}else if progDiff > 0{
if progDiff >= numberofPointsOnSlider / 2 {
animation = true
// if isIncrement {
// print("pointIndex : \(pointIndex)")
// pointIndex += pointIndex
// print("pointIndex Incremented : \(pointIndex)")
// }else{
// isIncrement = true
// }
pointIndex = Int(slider.value / numberofPointsOnSlider)
UIView.animate(withDuration: 10, animations: {
slider.value -= self.numberofPointsOnSlider - (self.lastSliderValue - slider.value)
})
mindfulDiff = Float(dataPoints[pointIndex].mindfulness - Double(mp))
relaxationDiff = Float(dataPoints[pointIndex].relaxation - Double(rp))
breathingDiff = Float(dataPoints[pointIndex].breathing - Double(bp))
doGraphAnimate()
}
else {
animation = true
UIView.animate(withDuration: 10, animations: {
slider.value = self.lastSliderValue
})
mindfulDiff = Float(dataPoints[pointIndex].mindfulness - Double(mp))
relaxationDiff = Float(dataPoints[pointIndex].relaxation - Double(rp))
breathingDiff = Float(dataPoints[pointIndex].breathing - Double(bp))
doGraphAnimate()
}
}
default :
break
}
}
}
Help me, Thankyou .
DO you mean you want slider to move only at certain discrete positions when you move it ? But your spec is not totally clear.
If so, this is how I, so that if slider moves from 0 to 14 it will show 14, from 15 to 85, it will show 85. I let you adapt to get the exact working you want.
I use this IBAction for the slider
#IBAction fileprivate func changeRowSlider(_ sender: NSSlider) {
let movedPosition = Int(sender.doubleValue)
if movedPosition == 0 {
sender.integerValue = 0
return
}
if movedPosition <= 14 {
sender.integerValue = 14
return
}
if movedPosition <= 85 {
sender.integerValue = 85
return
}
sender.integerValue = 100 // last case: move to maxValue
}
I have been trying to get the GLCameraRipple sample from Apple running in a swift project. Unfortunately this relies heavily on using C style, thread-safe arrays only available in Objective-C.
I have been trying to use a bridging header so that the simulation can run in Objective-C code, and the drawing can run in swift code. That way the thread-safe thing would not be an issue.
I have taken the Objective-C code and translated it almost completely into swift with a few exceptions. I did cull some of the extra math since the texture is the same size as the screen for my needs. If you want to check my translation I have put them below.
Anyway I have made a class that will run in any xcode project with an opengl environment.
import Foundation
import GLKit
import OpenGLES
class WaterDrawer
{
static var sim = RippleModel()
static var shade = Shader("Shader2")
static func pt(pt: CGPoint)
{
sim.initiateRippleAtLocation(pt)
}
static func firstStart(width: Int, height: Int)
{
sim.initWithScreenWidth(width / 4, iheight: height / 4, accWidth: width, accHeight: height)
shade.begin()
buildMatrix(width, height: height)
bufferSetup()
}
static func draw()
{
glUseProgram(shade.progId)
let posLoc = GLuint(glGetAttribLocation(shade.progId, "pos"))
let texLoc = GLuint(glGetAttribLocation(shade.progId, "tc"))
glBindBuffer(GLenum(GL_ARRAY_BUFFER), texVBO);
glBufferData(GLenum(GL_ARRAY_BUFFER), GLsizeiptr(sim.getVertexSize()), sim.getTexCoords(), GLenum(GL_DYNAMIC_DRAW));
glVertexAttribPointer(texLoc, 2, GLenum(GL_FLOAT), GLboolean(GL_FALSE), 0, BUFFER_OFFSET(0))
glEnableVertexAttribArray(texLoc)
glBindBuffer(GLenum(GL_ARRAY_BUFFER), posVBO)
glVertexAttribPointer(posLoc, 2, GLenum(GL_FLOAT), GLboolean(GL_FALSE), 0, BUFFER_OFFSET(0))
glEnableVertexAttribArray(posLoc)
let uniOrtho = glGetUniformLocation(shade.progId, "matrix")
glUniformMatrix4fv(uniOrtho, 1, GLboolean(GL_FALSE), &orthographicMatrix)
glBindBuffer(GLenum(GL_ELEMENT_ARRAY_BUFFER), indVBO)
glDrawElements(GLenum(GL_TRIANGLE_STRIP), GLsizei(sim.getIndexCount()), GLenum(GL_UNSIGNED_SHORT), nil)
glBindBuffer(GLenum(GL_ARRAY_BUFFER), 0)
glBindBuffer(GLenum(GL_ELEMENT_ARRAY_BUFFER), 0)
glDisableVertexAttribArray(posLoc)
glDisableVertexAttribArray(texLoc)
}
static func update()
{
sim.runSimulation()
}
static var posVBO:GLuint = 0
static var texVBO:GLuint = 0
static var indVBO:GLuint = 0
static func bufferSetup()
{
Whirl.crashLog("Started passing in buffer data")
glGenBuffers(1, &indVBO);
glBindBuffer(GLenum(GL_ELEMENT_ARRAY_BUFFER), indVBO);
glBufferData(GLenum(GL_ELEMENT_ARRAY_BUFFER), GLsizeiptr(sim.getIndexSize()), sim.getIndices(), GLenum(GL_STATIC_DRAW));
glGenBuffers(1, &posVBO);
glBindBuffer(GLenum(GL_ARRAY_BUFFER), posVBO);
glBufferData(GLenum(GL_ARRAY_BUFFER), GLsizeiptr(sim.getVertexSize()), sim.getVertices(), GLenum(GL_STATIC_DRAW));
glGenBuffers(1, &texVBO);
glBindBuffer(GLenum(GL_ARRAY_BUFFER), texVBO);
glBufferData(GLenum(GL_ARRAY_BUFFER), GLsizeiptr(sim.getVertexSize()), sim.getTexCoords(), GLenum(GL_DYNAMIC_DRAW));
Whirl.crashLog("Finished passing in buffer Data")
}
static var orthographicMatrix:[GLfloat] = []
static func buildMatrix(width: Int, height: Int)
{
orthographicMatrix = glkitmatrixtoarray(GLKMatrix4MakeOrtho(0, GLfloat(width), 0, GLfloat(height), -100, 100))
//Storage.upScaleFactor
}
static func glkitmatrixtoarray(mat: GLKMatrix4) -> [GLfloat]
{
var buildme:[GLfloat] = []
buildme.append(mat.m.0)
buildme.append(mat.m.1)
buildme.append(mat.m.2)
buildme.append(mat.m.3)
buildme.append(mat.m.4)
buildme.append(mat.m.5)
buildme.append(mat.m.6)
buildme.append(mat.m.7)
buildme.append(mat.m.8)
buildme.append(mat.m.9)
buildme.append(mat.m.10)
buildme.append(mat.m.11)
buildme.append(mat.m.12)
buildme.append(mat.m.13)
buildme.append(mat.m.14)
buildme.append(mat.m.15)
return buildme
}
}
So theoretically this code can use either the swift implementation or the Objective-C implementation, I just need to switch the way the mesh is initiated.
Trouble is when I use the Objective-C one the screen is blank, I have checked, and the buffer data looks really weird in the frame capture.
Are you allowed to pass in data from an Objective-C code to a glBuffer?
Simulation.swift
import Foundation
import GLKit
import OpenGLES
class RippleModel
{
var screenWidth:UInt32 = 0
var screenHeight:UInt32 = 0
var poolWidth:UInt32 = 0
var poolHeight:UInt32 = 0
var screenWidthi:Int = 0
var screenHeighti:Int = 0
var poolWidthi:Int = 0
var poolHeighti:Int = 0
let touchRadius:Int = 5 //5 i think
var rippleVertices:[GLfloat] = []
var rippleTexCoords:[GLfloat] = []
var rippleIndices:[GLushort] = []//NOTE IF CHANGE THIS TO INTO SO MUCH DRAW CALL
var rippleSource:[GLfloat] = []
var rippleDest:[GLfloat] = []
var rippleCoeff:[GLfloat] = []
var VertexSize:GLsizeiptr = 0
var IndicieSize:GLsizeiptr = 0
var IndicieCount:Int = 0
func calculateSizes()
{
VertexSize = rippleVertices.count * sizeof(GLfloat)
IndicieSize = rippleIndices.count * sizeof(GLushort)
IndicieCount = rippleIndices.count
Whirl.crashLog("Data sizes Vertex size \(VertexSize)\tIndicie Size \(IndicieSize) \tIndicie Count \(IndicieCount)")
}
func figureOutCoefficent()
{
for y in 0...(2 * touchRadius)
{
for x in 0...(2 * touchRadius)
{
let dx = x - touchRadius
let dy = y - touchRadius
let distance = sqrt(GLfloat(dx * dx + dy * dy))
let me = y * (touchRadius*2 + 1) + x
if (distance <= GLfloat(touchRadius))
{
let factor = distance / GLfloat(touchRadius)
rippleCoeff[me] = -(cos(factor*GLfloat(M_PI))+1.0) * 256.0;
}
else
{
rippleCoeff[me] = 0.0
}
}
}
}
init()
{
}
func initWithScreenWidth( iwidth: Int, iheight: Int, accWidth: Int, accHeight: Int)
{
screenWidth = UInt32(accWidth);screenWidthi = Int(screenWidth)
screenHeight = UInt32(accHeight);screenHeighti = Int(screenHeight)
poolWidth = UInt32(iwidth);poolWidthi = Int(poolWidth)
poolHeight = UInt32(iheight);poolHeighti = Int(poolHeight)
//WE DONT NEED TEX COORD MUMBO JUMBO IT IS FULL SCREEN ALREADY
Whirl.crashLog("Started allocation")
rippleCoeff = [GLfloat](count: Int( (touchRadius * 2 + 1) * (touchRadius*2 + 1) ), repeatedValue: 0)
figureOutCoefficent()
let simCount:Int = Int(poolWidth + 2) * Int(poolHeight + 2)
rippleSource = [GLfloat](count: simCount, repeatedValue: 0)
rippleDest = [GLfloat](count: simCount, repeatedValue: 0)
let locb:Int = Int(poolWidth * poolHeight * 2)
rippleVertices = [GLfloat](count: locb, repeatedValue: 0)
rippleTexCoords = [GLfloat](count: locb, repeatedValue: 0)
rippleIndices = [GLushort](count: Int(poolHeight - 1) * Int((poolWidth * 2) + 2), repeatedValue: 0)
Whirl.crashLog("Finished allocation")
initMesh()
calculateSizes()
}
func initMesh()
{
Whirl.crashLog("Started initting pos coords")
for i in 0..<poolHeight
{let ii = GLfloat(i)
for j in 0..<poolWidth
{let jj = GLfloat(j)
let cordb:Int = Int(i*poolWidth+j)*2
rippleVertices[cordb + 0] = (jj / GLfloat(poolWidth - 1)) * GLfloat(screenWidth)
rippleVertices[cordb + 1] = (ii / GLfloat(poolHeight - 1)) * GLfloat(screenHeight)
rippleTexCoords[cordb + 0] = ii / GLfloat(poolHeight - 1)
rippleTexCoords[cordb + 1] = (jj/GLfloat(poolWidth - 1))
}
}
Whirl.crashLog("Finished initting pos coords")
Whirl.crashLog("Started initting index coords")
var index = 0
for i in 0 ..< poolHeighti-1
{
for j in 0 ..< poolWidthi
{
if (i%2 == 0)
{
// emit extra index to create degenerate triangle
if (j == 0)
{
rippleIndices[index] = GLushort(i*poolWidthi+j);
index += 1;
}
rippleIndices[index] = GLushort(i*poolWidthi+j);
index += 1;
rippleIndices[index] = GLushort((i+1)*poolWidthi+j);
index += 1;
// emit extra index to create degenerate triangle
if (j == (poolWidthi-1))
{
rippleIndices[index] = GLushort((i+1)*poolWidthi+j);
index += 1;
}
}
else
{
// emit extra index to create degenerate triangle
if (j == 0)
{
rippleIndices[index] = GLushort((i+1)*poolWidthi+j);
index += 1;
}
rippleIndices[index] = GLushort((i+1)*poolWidthi+j);
index += 1;
rippleIndices[index] = GLushort(i * poolWidthi + j);
index += 1;
// emit extra index to create degenerate triangle
if (j == (poolWidthi-1))
{
rippleIndices[index] = GLushort(i * poolWidthi + j);
index += 1;
}
}
}
}
Whirl.crashLog("Finished initting coords")
}
var firstUpdate = true
func runSimulation()
{
if (firstUpdate)
{firstUpdate = false; Whirl.crashLog("First update")}
let queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
//dispatch_apply(Int(poolHeight), queue, {(y: size_t) -> Void in
for y in 0..<poolHeighti {
let pw = self.poolWidthi
for x in 1..<(pw - 1)
{
let ai:Int = (y ) * (pw + 2) + x + 1
let bi:Int = (y + 2) * (pw + 2) + x + 1
let ci:Int = (y + 1) * (pw + 2) + x
let di:Int = (y + 1) * (pw + 2) + x + 2
let me:Int = (y + 1) * (pw + 2) + x + 1
let a = self.rippleSource[ai]
let b = self.rippleSource[bi]
let c = self.rippleSource[ci]
let d = self.rippleSource[di]
var result = (a + b + c + d) / 2.0 - self.rippleDest[me]
result -= result / 32.0
self.rippleDest[me] = result
}
}
//)
let hm1 = GLfloat(poolHeight - 1)
let wm1 = GLfloat(poolWidth - 1)
//dispatch_apply(poolHeighti, queue, {(y: size_t) -> Void in
for y in 0..<poolHeighti{
let yy = GLfloat(y)
let pw = self.poolWidthi
for x in 1..<(pw - 1)
{let xx = GLfloat(x)
let ai:Int = (y ) * (pw + 2) + x + 1
let bi:Int = (y + 2) * (pw + 2) + x + 1
let ci:Int = (y + 1) * (pw + 2) + x
let di:Int = (y + 1) * (pw + 2) + x + 2
let a = self.rippleDest[ai]
let b = self.rippleDest[bi]
let c = self.rippleDest[ci]
let d = self.rippleDest[di]
var s_offset = ((b - a) / 2048)
var t_offset = ((c - d) / 2048)
s_offset = (s_offset < -0.5) ? -0.5 : s_offset;
t_offset = (t_offset < -0.5) ? -0.5 : t_offset;
s_offset = (s_offset > 0.5) ? 0.5 : s_offset;
t_offset = (t_offset > 0.5) ? 0.5 : t_offset;
let s_tc = yy / hm1
let t_tc = xx / wm1
let me = (y * pw + x) * 2
self.rippleTexCoords[me + 0] = s_tc + s_offset
self.rippleTexCoords[me + 1] = t_tc + t_offset
}
}
//)
let pTmp = rippleDest
rippleDest = rippleSource
rippleSource = pTmp
}
var firstRipple:Bool = true
func initiateRippleAtLocation(location: CGPoint)
{
if (firstRipple)
{firstRipple = false; Whirl.crashLog("First ripple placement")}
let xIndex = Int((GLfloat(location.x) / GLfloat(screenWidth)) * GLfloat(poolWidthi))
let yIndex = Int((1.0 - (GLfloat(location.y) / GLfloat(screenHeighti))) * GLfloat(poolHeight))
let lowy = yIndex - touchRadius
let highy = yIndex + touchRadius
let lowx = xIndex - touchRadius
let highx = xIndex + touchRadius
//Whirl.crashLog("Ripple at (\(xIndex) , \(yIndex))\tX:(\(lowx) - \(highx))\tY:(\(lowy) - \(highy))")
for y in lowy...highy
{
for x in lowx...highx
{
if (x > 0 && x < (poolWidthi - 1) && y > 0 && y < poolHeighti)
{
let ind = (poolWidthi + 2) * (y + 1) + x + 1
let coef = (y-(yIndex-touchRadius))*(touchRadius*2+1)+x-(xIndex-touchRadius)
let past = rippleSource[ind]
let coe = rippleCoeff[coef]
if (coe < past)
{
rippleSource[ind] = coe
}
}
}
}
}
func rippleLine(location1: CGPoint, location2: CGPoint)
{
if (firstRipple)
{firstRipple = false; Whirl.crashLog("First ripple placement")}
let xIndex1 = Int((GLfloat(location1.x) / GLfloat(screenWidth)) * GLfloat(poolWidthi))
let yIndex1 = Int((1.0 - (GLfloat(location1.y) / GLfloat(screenHeighti))) * GLfloat(poolHeight))
let xIndex2 = Int((GLfloat(location2.x) / GLfloat(screenWidth)) * GLfloat(poolWidthi))
let yIndex2 = Int((1.0 - (GLfloat(location2.y) / GLfloat(screenHeighti))) * GLfloat(poolHeight))
let lowy1 = yIndex1 - touchRadius
let highy1 = yIndex1 + touchRadius
let lowx1 = xIndex1 - touchRadius
let highx1 = xIndex1 + touchRadius
let lowy2 = yIndex2 - touchRadius
let highy2 = yIndex2 + touchRadius
let lowx2 = xIndex2 - touchRadius
let highx2 = xIndex2 + touchRadius
let lowx = min(lowx1, lowx2)
let highx = max(highx1, highx2)
let lowy = min(lowy1, lowy2)
let highy = max(highy1, highy2)
for y in lowy...highy
{
for x in lowx...highx
{
if (x > 0 && x < (poolWidthi - 1) && y > 0 && y < poolHeighti)
{
let ind = (poolWidthi + 2) * (y + 1) + x + 1
let tar = ldist(CGPoint(x: xIndex1, y: yIndex1), p2: CGPoint(x: xIndex2, y: yIndex2), me: CGPoint(x: x, y: y))
let dx = x - Int(tar.x)
let dy = y - Int(tar.y)
let distq = (dx * dx + dy * dy)
if (distq < touchRadius * touchRadius)
{
let factor = sqrt(GLfloat(distq)) / GLfloat(touchRadius)
rippleSource[ind] = -(cos(factor*GLfloat(M_PI))+1.0) * 256.0;
}
//rippleSource[ind] = 1000
}
}
}
}
func ldist(p1: CGPoint, p2: CGPoint, me: CGPoint) -> CGPoint
{
let diffX = p2.x - p1.x
let diffY = p2.y - p1.y
var target = CGPoint()
if ((diffX == 0) && (diffY == 0))
{
target = p1
}
let t = ((me.x - p1.x) * diffX + (me.y - p1.y) * diffY) / (diffX * diffX + diffY * diffY)
if (t < 0)
{
target = p1
}
else if (t > 1)
{
target = p2
}
else
{
target = CGPoint(x: (p1.x + t * diffX), y: (p1.y + t * diffY))
}
let int = CGPoint(x: round(target.x), y: round(target.y))
return int
}
func getVertices() -> [GLfloat]
{
//Return the mesh positions
return rippleVertices
}
func getTexCoords() -> [GLfloat]
{
//Return the array of texture coords
return rippleTexCoords
}
func getIndices() -> [GLushort]
{
//Return the array of indices
return rippleIndices
}
func getVertexSize() -> GLsizeiptr
{
//Return the size of the mesh position array
return VertexSize
}
func getIndexSize() -> GLsizeiptr
{
//Return the byte size of the incicie array
return IndicieSize
}
func getIndexCount() -> GLsizeiptr
{
//This goes in the draw call, count of indices
return IndicieCount
}
}
RippleModel.m (from apple)
Are you allowed to pass in data from an objective-c code to a glBuffer?
Why wouldn't you be allowed? Swift has a pointer API (UnsafePointer<T>, UnsafeMutablePointer<T>, etc.) exactly for this purpose. Obviously this is "unsafe" in the sense that the underlying memory the [Objective-]C pointer points to could change at anytime without the Swift pointer knowing. It also has no information about the size of the memory block that it points to.
Any C pointers or arrays can be bridged to Swift (probably as UnsafeMutablePointer<Void> which you will need to cast to your OpenGL type).
You can avoid any risk of referencing invalid memory by dereferencing the pointer (if it is non-nil) and copying the value stored at the pointer to a variable in your Swift application.
With OpenGL ES being a pure set of C functions, I don't think passing a pointer of swift datatypes will work easily.
The following code will give you a hint of how to pass the index buffer.
var Indices: [GLubyte] = [
0, 1, 2,
2, 3, 0
]
var indexBuffer: GLuint = GLuint()
glGenBuffers(1, &indexBuffer)
glBindBuffer(GLenum(GL_ELEMENT_ARRAY_BUFFER), indexBuffer)
glBufferData(GLenum(GL_ELEMENT_ARRAY_BUFFER), Indices.size(), Indices, GLenum(GL_STATIC_DRAW))
Reference: Here is a link with a working code. https://github.com/bradley/iOSSwiftOpenGL/blob/master/iOSSwiftOpenGL/OpenGLView.swift
Below I have written some bounding box logic which is a bit verbose. Is there a way to make it more readable, so the check looks cleaner and more concise?
func validatePosition(position:SCNVector3, forNode node:SCNNode) -> SCNVector3 {
var newPosition = position
var maxVector = SCNVector3Zero
var minVector = SCNVector3Zero
let success = self.actionDelegate?.getBoundingBox(&minVector, max: &maxVector)
guard success == true else {
return newPosition
}
if newPosition.x < minVector.x && newPosition.x < 0 {
newPosition.x = minVector.x
}
if newPosition.y < minVector.y && newPosition.y < 0 {
newPosition.y = minVector.y
}
if newPosition.z < minVector.z && newPosition.z < 0 {
newPosition.z = minVector.z
}
if newPosition.x > maxVector.x && newPosition.x > 0 {
newPosition.x = maxVector.x
}
if newPosition.y > maxVector.y && newPosition.y > 0 {
newPosition.y = maxVector.y
}
if newPosition.z > maxVector.z && newPosition.z > 0 {
newPosition.z = maxVector.z
}
return newPosition
}
Try this:
extension ClosedInterval {
func clamp(value : Bound) -> Bound {
return self.start > value ? self.start
: self.end < value ? self.end
: value
}
}
extension SCNVector3 {
func clamp(min min:SCNVector3, max: SCNVector3) -> SCNVector3 {
let x = (min.x...max.x).clamp(self.x)
let y = (min.y...max.y).clamp(self.y)
let z = (min.z...max.z).clamp(self.z)
return SCNVector3(x, y, z)
}
}
func validatePosition(position:SCNVector3, forNode node:SCNNode) -> SCNVector3 {
var newPosition = position
var maxVector = SCNVector3Zero
var minVector = SCNVector3Zero
let success = self.actionDelegate?.getBoundingBox(&minVector, max: &maxVector)
guard success == true else {
return newPosition
}
newPosition = position.clamp(min: minVector, max: maxVector)
return newPosition
}
I'm working on a financial application that has some quite large numbers.
One of my charts for example is growing from $25,000 to about $20,000,000,000 (exponentially)
Because of this, using any regular chart without log-scale causes the majority of my data to simply "flatline", while it's in fact growing quickly.
Here's an example
(Ignore that it's not set up yet)
The above linechart was made using SwiftChart
Now I have to be honest, I am not even at this point entirely sure if I'm even allowed to ask a question like this on SO. But I just have no idea where to begin.
After a couple of hours of searching I have not been able to find any libraries, tutorials, sample apps or anything IOS related that features a graph with a logarithmic axis (neither X or Y).
After I couldn't find anything, I realised I'd have to implement this feature on my own. I found the most suitable library for my application, and I've been trying to figure out how I can implement a way to allow the y-scale to be scaled with a log-scale (with the power of 10).
I've done a fair bit of research on how log-scales work, and used them plenty in my university. But I have never actually seen an example of one programmed.
I found one written in JavaScript (I think). But unfortunately I have no experience with web-development and wasn't able to properly translate it :(
I really don't feel comfortable putting someone elses code on SO, but I really have no clue where to begin.
If this isn't allowed, please post a comment, and I'll get rid of it immediately
All credits of this goes to this guy: https://github.com/gpbl/SwiftChart
Here's the code that I believe is where I need to implement a log-scale.
private func drawChart() {
drawingHeight = bounds.height - bottomInset - topInset
drawingWidth = bounds.width
let minMax = getMinMax()
min = minMax.min
max = minMax.max
highlightShapeLayer = nil
// Remove things before drawing, e.g. when changing orientation
for view in self.subviews {
view.removeFromSuperview()
}
for layer in layerStore {
layer.removeFromSuperlayer()
}
layerStore.removeAll()
// Draw content
for (index, series) in enumerate(self.series) {
// Separate each line in multiple segments over and below the x axis
var segments = Chart.segmentLine(series.data as ChartLineSegment)
for (i, segment) in enumerate(segments) {
let scaledXValues = scaleValuesOnXAxis( segment.map( { return $0.x } ) )
let scaledYValues = scaleValuesOnYAxis( segment.map( { return $0.y } ) )
if series.line {
drawLine(xValues: scaledXValues, yValues: scaledYValues, seriesIndex: index)
}
if series.area {
drawArea(xValues: scaledXValues, yValues: scaledYValues, seriesIndex: index)
}
}
}
drawAxes()
if xLabels != nil || series.count > 0 {
drawLabelsAndGridOnXAxis()
}
if yLabels != nil || series.count > 0 {
drawLabelsAndGridOnYAxis()
}
}
// MARK: - Scaling
private func getMinMax() -> (min: ChartPoint, max: ChartPoint) {
// Start with user-provided values
var min = (x: minX, y: minY)
var max = (x: maxX, y: maxY)
// Check in datasets
for series in self.series {
let xValues = series.data.map( { (point: ChartPoint) -> Float in
return point.x } )
let yValues = series.data.map( { (point: ChartPoint) -> Float in
return point.y } )
let newMinX = minElement(xValues)
let newMinY = minElement(yValues)
let newMaxX = maxElement(xValues)
let newMaxY = maxElement(yValues)
if min.x == nil || newMinX < min.x! { min.x = newMinX }
if min.y == nil || newMinY < min.y! { min.y = newMinY }
if max.x == nil || newMaxX > max.x! { max.x = newMaxX }
if max.y == nil || newMaxY > max.y! { max.y = newMaxY }
}
// Check in labels
if xLabels != nil {
let newMinX = minElement(xLabels!)
let newMaxX = maxElement(xLabels!)
if min.x == nil || newMinX < min.x { min.x = newMinX }
if max.x == nil || newMaxX > max.x { max.x = newMaxX }
}
if yLabels != nil {
let newMinY = minElement(yLabels!)
let newMaxY = maxElement(yLabels!)
if min.y == nil || newMinY < min.y { min.y = newMinY }
if max.y == nil || newMaxY > max.y { max.y = newMaxY }
}
if min.x == nil { min.x = 0 }
if min.y == nil { min.y = 0 }
if max.x == nil { max.x = 0 }
if max.y == nil { max.y = 0 }
return (min: (x: min.x!, y: min.y!), max: (x: max.x!, max.y!))
}
private func scaleValuesOnXAxis(values: Array<Float>) -> Array<Float> {
let width = Float(drawingWidth)
var factor: Float
if max.x - min.x == 0 { factor = 0 }
else { factor = width / (max.x - min.x) }
let scaled = values.map { factor * ($0 - self.min.x) }
return scaled
}
private func scaleValuesOnYAxis(values: Array<Float>) -> Array<Float> {
let height = Float(drawingHeight)
var factor: Float
if max.y - min.y == 0 { factor = 0 }
else { factor = height / (max.y - min.y) }
let scaled = values.map { Float(self.topInset) + height - factor * ($0 - self.min.y) }
return scaled
}
private func scaleValueOnYAxis(value: Float) -> Float {
let height = Float(drawingHeight)
var factor: Float
if max.y - min.y == 0 { factor = 0 }
else { factor = height / (max.y - min.y) }
let scaled = Float(self.topInset) + height - factor * (value - min.y)
return scaled
}
private func getZeroValueOnYAxis() -> Float {
if min.y > 0 {
return scaleValueOnYAxis(min.y)
}
else {
return scaleValueOnYAxis(0)
}
}
// MARK: - Drawings
private func isVerticalSegmentAboveXAxis(yValues: Array<Float>) -> Bool {
// YValues are "reverted" from top to bottom, so min is actually the maxz
let min = maxElement(yValues)
let zero = getZeroValueOnYAxis()
return min <= zero
}
private func drawLine(#xValues: Array<Float>, yValues: Array<Float>, seriesIndex: Int) -> CAShapeLayer {
let isAboveXAxis = isVerticalSegmentAboveXAxis(yValues)
let path = CGPathCreateMutable()
CGPathMoveToPoint(path, nil, CGFloat(xValues.first!), CGFloat(yValues.first!))
for i in 1..<yValues.count {
let y = yValues[i]
CGPathAddLineToPoint(path, nil, CGFloat(xValues[i]), CGFloat(y))
}
var lineLayer = CAShapeLayer()
lineLayer.frame = self.bounds
lineLayer.path = path
if isAboveXAxis {
lineLayer.strokeColor = series[seriesIndex].colors.above.CGColor
}
else {
lineLayer.strokeColor = series[seriesIndex].colors.below.CGColor
}
lineLayer.fillColor = nil
lineLayer.lineWidth = lineWidth
lineLayer.lineJoin = kCALineJoinBevel
self.layer.addSublayer(lineLayer)
layerStore.append(lineLayer)
return lineLayer
}
private func drawArea(#xValues: Array<Float>, yValues: Array<Float>, seriesIndex: Int) {
let isAboveXAxis = isVerticalSegmentAboveXAxis(yValues)
let area = CGPathCreateMutable()
let zero = CGFloat(getZeroValueOnYAxis())
CGPathMoveToPoint(area, nil, CGFloat(xValues[0]), zero)
for i in 0..<xValues.count {
CGPathAddLineToPoint(area, nil, CGFloat(xValues[i]), CGFloat(yValues[i]))
}
CGPathAddLineToPoint(area, nil, CGFloat(xValues.last!), zero)
var areaLayer = CAShapeLayer()
areaLayer.frame = self.bounds
areaLayer.path = area
areaLayer.strokeColor = nil
if isAboveXAxis {
areaLayer.fillColor = series[seriesIndex].colors.above.colorWithAlphaComponent(areaAlphaComponent).CGColor
}
else {
areaLayer.fillColor = series[seriesIndex].colors.below.colorWithAlphaComponent(areaAlphaComponent).CGColor
}
areaLayer.lineWidth = 0
self.layer.addSublayer(areaLayer)
layerStore.append(areaLayer)
}
private func drawAxes() {
let context = UIGraphicsGetCurrentContext()
CGContextSetStrokeColorWithColor(context, axesColor.CGColor)
CGContextSetLineWidth(context, 0.5)
// horizontal axis at the bottom
CGContextMoveToPoint(context, 0, drawingHeight + topInset)
CGContextAddLineToPoint(context, drawingWidth, drawingHeight + topInset)
CGContextStrokePath(context)
// horizontal axis at the top
CGContextMoveToPoint(context, 0, 0)
CGContextAddLineToPoint(context, drawingWidth, 0)
CGContextStrokePath(context)
// horizontal axis when y = 0
if min.y < 0 && max.y > 0 {
let y = CGFloat(getZeroValueOnYAxis())
CGContextMoveToPoint(context, 0, y)
CGContextAddLineToPoint(context, drawingWidth, y)
CGContextStrokePath(context)
}
// vertical axis on the left
CGContextMoveToPoint(context, 0, 0)
CGContextAddLineToPoint(context, 0, drawingHeight + topInset)
CGContextStrokePath(context)
// vertical axis on the right
CGContextMoveToPoint(context, drawingWidth, 0)
CGContextAddLineToPoint(context, drawingWidth, drawingHeight + topInset)
CGContextStrokePath(context)
}
Any hint or help as to where I need to implement this would be massively appreciated.
And I apologize for the awfulness that is this question begging for help or a direction.
In my adding function where answerLabel.text is it is giving me an error. It says 'String' is not convertible to 'Int' and I'm trying to get what I got from the secondStep2 and put in the parameters of my functions
//Adding Function
changingSignsLabel.text = "+"
let firstDenomInTextField:Double! = (firstDenominatorTextField.text as NSString).doubleValue
let firstNumInTextField:Double! = (firstNumeratorTextField.text as NSString).doubleValue
let firstWholeInTextField:Double! = (firstWholeNumberTextField.text as NSString).doubleValue
let secondDenomInTextField:Double! = (secondDenominatorTextField.text as NSString).doubleValue
let secondNumInTextField:Double! = (secondNumeratorTextField.text as NSString).doubleValue
let secondWholeInTextField:Double! = (secondWholeNumberTextField.text as NSString).doubleValue
var firstStep = firstDenomInTextField! * firstWholeInTextField! / firstDenomInTextField!
var secondStep = firstStep + firstNumInTextField! / firstDenomInTextField!
var thirdStep = secondDenomInTextField! * secondWholeInTextField! / secondDenomInTextField!
var fourthStep = thirdStep + secondNumInTextField! / secondDenomInTextField!
var calculatedAnswer = (secondStep + fourthStep)
answerLabel.hidden = false
var firstStep2 = calculatedAnswer / 1
var secondStep2 = "\(firstStep2 * 10 * 10)"
answerLabel.text = printSimplifiedFraction(Numerator: secondStep2)
My Function
func printSimplifiedFraction(Numerator numerator: Int, Denominator denominator: Int = 100)
{
var finalNumerator = numerator;
var finalDenominator = denominator;
var wholeNumbers:Int = numerator / denominator;
var remainder:Int = numerator % denominator;
//println("wholeNumbers = \(wholeNumbers), remainder = \(remainder)");
//println("\(denominator) % \(remainder) = \(denominator % remainder)");
if(remainder > 0)
{
// see if we can simply the fraction part as well
if(denominator % remainder == 0) // no remainder means remainder can be simplified further
{
finalDenominator = denominator / remainder;
finalNumerator = remainder / remainder;
}
else
{
finalNumerator = remainder;
finalDenominator = denominator;
}
}
if(wholeNumbers > 0 && remainder > 0)
{
// prints out whole number and fraction parts
println("Simplified fraction of \(numerator)/\(denominator) = \(wholeNumbers) \(finalNumerator)/\(finalDenominator)");
}
else if (wholeNumbers > 0 && remainder == 0)
{
// prints out whole number only
println("Simplified fraction of \(numerator)/\(denominator) = \(wholeNumbers)");
}
else
{
// prints out fraction part only
println("Simplified fraction of \(numerator)/\(denominator) = \(finalNumerator)/\(finalDenominator)");
}
}
My Question how do I get the function to accept my variable?
I believe this is what you want, first in your adding function it should be:
var firstStep2 = calculatedAnswer / 1
var secondStep2 = Int(firstStep2 * 10 * 10)
answerLabel.text = printSimplifiedFraction(Numerator: secondStep2)
Then your print... method should be changed like this (note that it's returning a String):
func printSimplifiedFraction(Numerator numerator: Int, Denominator denominator: Int = 100) -> String
{
// I haven't looked into this bit
...
if(wholeNumbers > 0 && remainder > 0)
{
return ("Simplified fraction of \(numerator)/\(denominator) = \(wholeNumbers) \(finalNumerator)/\(finalDenominator)")
}
else if (wholeNumbers > 0 && remainder == 0)
{
return ("Simplified fraction of \(numerator)/\(denominator) = \(wholeNumbers)")
}
else
{
return ("Simplified fraction of \(numerator)/\(denominator) = \(finalNumerator)/\(finalDenominator)")
}
}
Also, looking through your code I think you can simplify the logic quite a bit (I've also converted to Integers as you said you wanted in the comments):
let firstDenomInTextField = firstDenominatorTextField.text.toInt()
let firstNumInTextField = firstNumeratorTextField.text.toInt()
let firstWholeInTextField = firstWholeNumberTextField.text.toInt()
let secondDenomInTextField = secondDenominatorTextField.text.toInt()
let secondNumInTextField = secondNumeratorTextField.text.toInt()
let secondWholeInTextField = secondWholeNumberTextField.text.toInt()
var firstStep = firstWholeInTextField! + firstNumInTextField! / firstDenomInTextField!
var secondStep = secondWholeInTextField! + secondNumInTextField! / secondDenomInTextField!
var calculatedAnswer = (firstStep + secondStep)
var numerator = Int(calculatedAnswer * 10 * 10)
answerLabel.text = printSimplifiedFraction(Numerator: numerator)
Try this:
var str:String = "abc"
var a:Int? = str.toInt()
if (a != nil) {
printSimplifiedFraction(Numerator: str!)
}
And add a return value to your function
func printSimplifiedFraction(Numerator numerator: Int, Denominator denominator: Int = 100) ->String {
var finalNumerator = numerator;
var finalDenominator = denominator;
var wholeNumbers:Int = numerator / denominator;
var remainder:Int = numerator % denominator;
if(remainder > 0)
{
// see if we can simply the fraction part as well
if(denominator % remainder == 0) // no remainder means remainder can be simplified further
{
finalDenominator = denominator / remainder;
finalNumerator = remainder / remainder;
}
else
{
finalNumerator = remainder;
finalDenominator = denominator;
}
}
var result:String = "Simplified fraction of \(numerator)/\(denominator) = ";
if(wholeNumbers > 0 && remainder > 0)
{
// prints out whole number and fraction parts
result += "\(wholeNumbers) \(finalNumerator)/\(finalDenominator)"
}
else if (wholeNumbers > 0 && remainder == 0)
{
// prints out whole number only
result += "\(wholeNumbers)"
}
else
{
// prints out fraction part only
result += "\(finalNumerator)/\(finalDenominator)"
}
println("\(result)")
return result
}
this printSimplifiedFraction is not returning any value, but you are assigning to the answerLabel.text, i think this is the problem.
If you want to assign a value which is returned form fuction, add return type to the fuction and return a value.
func printSimplifiedFraction(Numerator numerator: Int, Denominator denominator: Int = 100) -> String {
//add end of your function
return yourValue;
}
Check this link
In Your Code do like this
var secondStep2 = (firstStep2 * 10 * 10)
answerLabel.text = printSimplifiedFraction(Numerator: secondStep2)
func printSimplifiedFraction(Numerator numerator: Int, Denominator denominator: Int = 100) -> String
{
var finalNumerator = numerator;
var finalDenominator = denominator;
var wholeNumbers:Int = numerator / denominator;
var remainder:Int = numerator % denominator;
//println("wholeNumbers = \(wholeNumbers), remainder = \(remainder)");
//println("\(denominator) % \(remainder) = \(denominator % remainder)");
if(remainder > 0)
{
// see if we can simply the fraction part as well
if(denominator % remainder == 0) // no remainder means remainder can be simplified further
{
finalDenominator = denominator / remainder;
finalNumerator = remainder / remainder;
}
else
{
finalNumerator = remainder;
finalDenominator = denominator;
}
}
var returnValue :String
if(wholeNumbers > 0 && remainder > 0)
{
returnValue = "Simplified fraction of \(numerator)/\(denominator) = \(wholeNumbers) \(finalNumerator)/\(finalDenominator)"
}
else if (wholeNumbers > 0 && remainder == 0)
{
returnValue = "Simplified fraction of \(numerator)/\(denominator) = \(wholeNumbers)"
}
else
{
returnValue = "Simplified fraction of \(numerator)/\(denominator) = \(finalNumerator)/\(finalDenominator)"
}
return returnValue
}