How do I make an analog clock using winforms in F#? - winforms

I have to create an analog clock using winforms in F#. The clock needs also to have a label showing date and time in digital form. I have figured how make the label with time and date, as well as drawing the circle and the clockhands, but I'm having trouble with how i'm gonna implement the timer function to the clock hands. So they move, and move to what the time is right now.
I don't know how to call the things i need in the third last line.
Is there anyone who can help me with this problem?
Because as it stands right now I have no idea how to it.
open System
open System.Windows.Forms
open System.ComponentModel
open System.Drawing
// ********* Winforms specifics *********
let win = new Form()
win.ClientSize <- Size (400, 400)
/// ********* Working Digital Clock *********
// make a label to show time
let digitalTimerLabel = new Label ()
win.Controls.Add digitalTimerLabel
digitalTimerLabel.Width <- 200
digitalTimerLabel.Location <- new Point (140,300)
digitalTimerLabel.Text <- string System.DateTime.Now // get present time and date
// make a timer and link to label
let timer = new Timer ()
timer.Interval <- 1000 // create an event every 1000 millisecond
timer.Enabled <- true // activiate the timer
timer.Tick.Add (fun e ->
digitalTimerLabel.Text <- string System.DateTime.Now
win.Invalidate()
)
// ********* Translate the clock *********
let translate (d : Point) (arr : Point []) : Point [] =
let add (d : Point) (p : Point) : Point =
Point (d.X + p.X, d.Y + p.Y)
Array.map (add d) arr
// ********* Rotate the clock hands *********
let rotate (theta : float) (arr : Point []) : Point [] =
let toInt = int << round
let rot (t : float) (p : Point) : Point =
let (x, y) = (float p.X, float p.Y)
let (a, b) = (x * cos t - y * sin t, x * sin t + y * cos t)
Point (toInt a, toInt b)
Array.map (rot theta) arr
/// ********* ClockHands (Ur-visere) *********
let myPaint (e : PaintEventArgs) : unit =
// HourHand
let black = new Pen (Color.Black,Width=2.0f)
let hourHand =
// [bot cord] [top cord]
[|Point (0,0);Point (0,-45)|]
e.Graphics.DrawLines (black, hourHand)
// MinuteHand
let red = new Pen (Color.Red,Width=4.0f)
let minuteHand =
// [bot cord] [top cord]
[|Point (0,0);Point (0,-20)|]
e.Graphics.DrawLines (red, minuteHand)
// SecondHand
let green = new Pen (Color.Green,Width=1.0f)
let secondHand =
// [bot cord] [top cord]
[|Point (0,0);Point (0,-20)|]
e.Graphics.DrawLines (green, secondHand)
// Circle
let circleBlack = new Pen(Color.Black,Width=4.0f)
let circle =
e.Graphics.DrawEllipse(circleBlack,-100.0f,-100.0f,200.0f,200.0f)
circle
// CenterDot
let CenterDotBrush = new SolidBrush(Color.Red)
let center =
e.Graphics.FillEllipse(CenterDotBrush,-2.5f,-2.5f,5.0f,5.0f)
center
let dt = DateTime.Now
let s = dt.Second
let m = dt.Minute
let h = dt.Hour
let newS = rotate (float s/60.0*2.0*System.Math.PI) secondHand
let newM = rotate (float m/60.0*2.0*System.Math.PI) minuteHand
let newH = rotate (float h/12.0*2.0*System.Math.PI) hourHand
let finalS = translate (Point (200, 200)) secondHand
let finalM = translate (Point (200, 200)) minuteHand
let finalH = translate (Point (200, 200)) hourHand
()
win.Paint.Add myPaint
Application.Run win // start event - loop

I have sorted it out. It works now:
open System
open System.Windows.Forms
open System.Drawing
// ********* Winforms specifics *********
// Extended the default Form to avoid display flickered
type SmoothForm() as x =
inherit Form()
do x.DoubleBuffered <- true
let win = new SmoothForm()
win.ClientSize <- Size (400, 400)
/// ********* Digital Clock *********
// make a label to show time
let digitalTimerLabel = new Label ()
win.Controls.Add digitalTimerLabel
digitalTimerLabel.Width <- 200
digitalTimerLabel.Location <- Point (150,320)
// Timer
let timer = new Timer ()
timer.Interval <- 1000 // create an event every 1000 millisecond
timer.Enabled <- true // activiate the timer
timer.Tick.Add (fun _e ->
digitalTimerLabel.Text <- string System.DateTime.Now
win.Invalidate()
)
// ********* Translate function *********
let translate (d : Point) (arr : Point []) : Point [] =
let add (d : Point) (p : Point) : Point =
Point (d.X + p.X, d.Y + p.Y)
Array.map (add d) arr
// ********* Rotate the clock hands *********
let rotate (theta : float) (arr : Point []) : Point [] =
let toInt = int << round
let rot (t : float) (p : Point) : Point =
let (x, y) = (float p.X, float p.Y)
let (a, b) = (x * cos t - y * sin t, x * sin t + y * cos t)
Point (toInt a, toInt b)
Array.map (rot theta) arr
/// ********* ClockHands (Ur-visere) *********
let myPaint (e : PaintEventArgs) : unit =
// HourHand
let black = new Pen (Color.Black,Width=4.0f)
let hourHand =
[|Point (0,0); Point (0,-60)|]
// MinuteHand
let red = new Pen (Color.Red,Width=4.0f)
let minuteHand =
[|Point (0,0);Point (0,-90)|]
// SecondHand
let green = new Pen (Color.Green,Width=1.0f)
let secondHand =
[|Point (0,0);Point (0,-90)|]
// Circle
let circleBlack = new Pen(Color.Black,Width=4.0f)
let circle =
e.Graphics.DrawEllipse(circleBlack,100.0f,100.0f,200.0f,200.0f)
circle
//Circle color
let circlecolorBrush = new SolidBrush(Color.LightSalmon)
let circle =
e.Graphics.FillEllipse(circlecolorBrush,100.0f,100.0f,200.0f,200.0f)
circle
// CenterDot
let centerDotBrush = new SolidBrush(Color.Red)
let center =
e.Graphics.FillEllipse(centerDotBrush,197.5f,197.5f,5.0f,5.0f)
center
// Time
let dt = DateTime.Now
let s = dt.Second
let m = dt.Minute
let h = dt.Hour
// Make Rotation
let secondHand = rotate (float s/60.0*2.0*System.Math.PI) secondHand
let minuteHand = rotate (float m/60.0*2.0*System.Math.PI) minuteHand
let hourHand = rotate (float h/12.0*2.0*System.Math.PI) hourHand
// Make Translate (Moving Point)
let secondHand = translate (Point (200, 200)) secondHand
let minuteHand = translate (Point (200, 200)) minuteHand
let hourHand = translate (Point (200, 200)) hourHand
e.Graphics.DrawLines (black, hourHand)
e.Graphics.DrawLines (red, minuteHand)
e.Graphics.DrawLines (green, secondHand)
win.Paint.Add myPaint
Application.Run win // start event - loop

Related

How to change colors of a cell of collection view on scrolling swift5

i want to change color of the cell of my collectionview on scrolling in swift.
here is an image of my viewcontroller. i want to change background color of the specific cell to white on scroll. and i used animatedcollectionViewLayout for animation.
This may help you, call it in scrollViewDidScroll. Don't use indexPathsForVisibleItems、visibleCells, they have a few issues.
func updateCellsApperance() {
let visibleRect = CGRect(origin: modesCollectionView.contentOffset, size: modesCollectionView.frame.size)
let center = CGPoint(x: visibleRect.midX, y: visibleRect.midY)
var indexPaths: Set<IndexPath> = []
let step = 10.0
var x = center.x
while x > visibleRect.minX {
if let indexPath = modesCollectionView.indexPathForItem(at: CGPoint(x: x, y: center.y)) {
indexPaths.insert(indexPath)
}
x -= step
}
x = center.x + step
while x < visibleRect.maxX {
if let indexPath = modesCollectionView.indexPathForItem(at: CGPoint(x: x, y: center.y)) {
indexPaths.insert(indexPath)
}
x += step
}
indexPaths.forEach { indexPath in
if let cell = modesCollectionView.cellForItem(at: indexPath),
let attrs = modesCollectionView.layoutAttributesForItem(at: indexPath) {
let distanceX = visibleRect.midX - attrs.center.x
// >= 0.0
let ratio = abs(distanceX) * (1.0 / attrs.frame.width)
cell.xxx.color = UIColor(red: 0.0, green: max(0.0, 1.0 - ratio), blue: 0, alpha: 1.0)
}
}
}

Graphic Points are not working outside of viewDidLoad

I've created a Graphic Class to show for user a graphic points choose accordingly with user information.
If I use the graphicView object and add points at viewDidLoad, the graphic is presented correctly, if not there, the graphic presents no data.
See below the code for the Graphics and the code when I am requesting to mark the points.
class GraphView: UIView {
private struct Constants {
static let cornerRadiusSize = CGSize(width: 8.0, height: 8.0)
static let margin: CGFloat = 40.0
static let topBorder: CGFloat = 30
static let bottomBorder: CGFloat = 40
static let colorAlpha: CGFloat = 0.3
static let circleDiameter: CGFloat = 5.0
}
//1 - the properties for the gradient
var startColor: UIColor = UIColor.rgb(red: 14, green: 40, blue: 80)
var endColor: UIColor = UIColor.rgb(red: 14, green: 40, blue: 80)
//Weekly sample data
var graphPoints: [Int] = [0]
override func draw(_ rect: CGRect) {
let width = rect.width
let height = rect.height
let path = UIBezierPath(roundedRect: rect,
byRoundingCorners: UIRectCorner.allCorners,
cornerRadii: Constants.cornerRadiusSize)
path.addClip()
//2 - get the current context
let context = UIGraphicsGetCurrentContext()!
let colors = [startColor.cgColor, endColor.cgColor]
//3 - set up the color space
let colorSpace = CGColorSpaceCreateDeviceRGB()
//4 - set up the color stops
let colorLocations: [CGFloat] = [0.0, 1.0]
//5 - create the gradient
let gradient = CGGradient(colorsSpace: colorSpace,
colors: colors as CFArray,
locations: colorLocations)!
//6 - draw the gradient
var startPoint = CGPoint.zero
var endPoint = CGPoint(x: 0, y: self.bounds.height)
context.drawLinearGradient(gradient,
start: startPoint,
end: endPoint,
options: CGGradientDrawingOptions(rawValue: 0))
//calculate the x point
let margin = Constants.margin
let columnXPoint = { (column:Int) -> CGFloat in
//Calculate gap between points
let spacer = (width - margin * 2 - 4) / CGFloat((self.graphPoints.count - 1))
var x: CGFloat = CGFloat(column) * spacer
x += margin + 2
return x
}
// calculate the y point
let topBorder: CGFloat = Constants.topBorder
let bottomBorder: CGFloat = Constants.bottomBorder
let graphHeight = height - topBorder - bottomBorder
let maxValue = graphPoints.max()!
let columnYPoint = { (graphPoint:Int) -> CGFloat in
var y:CGFloat = CGFloat(graphPoint) / CGFloat(maxValue) * graphHeight
y = graphHeight + topBorder - y // Flip the graph
return y
}
// draw the line graph
UIColor.white.setFill()
UIColor.white.setStroke()
//set up the points line
let graphPath = UIBezierPath()
//go to start of line
graphPath.move(to: CGPoint(x:columnXPoint(0), y:columnYPoint(graphPoints[0])))
//add points for each item in the graphPoints array
//at the correct (x, y) for the point
for i in 1..<graphPoints.count {
let nextPoint = CGPoint(x:columnXPoint(i), y:columnYPoint(graphPoints[i]))
graphPath.addLine(to: nextPoint)
}
//Create the clipping path for the graph gradient
//1 - save the state of the context (commented out for now)
context.saveGState()
//2 - make a copy of the path
let clippingPath = graphPath.copy() as! UIBezierPath
//3 - add lines to the copied path to complete the clip area
clippingPath.addLine(to: CGPoint(x: columnXPoint(graphPoints.count - 1), y:height))
clippingPath.addLine(to: CGPoint(x:columnXPoint(0), y:height))
clippingPath.close()
//4 - add the clipping path to the context
clippingPath.addClip()
let highestYPoint = columnYPoint(maxValue)
startPoint = CGPoint(x:margin, y: highestYPoint)
endPoint = CGPoint(x:margin, y:self.bounds.height)
context.drawLinearGradient(gradient, start: startPoint, end: endPoint, options: CGGradientDrawingOptions(rawValue: 0))
context.restoreGState()
//draw the line on top of the clipped gradient
graphPath.lineWidth = 3.0
graphPath.stroke()
//Draw the circles on top of graph stroke
for i in 0..<graphPoints.count {
var point = CGPoint(x:columnXPoint(i), y:columnYPoint(graphPoints[i]))
point.x -= Constants.circleDiameter / 2
point.y -= Constants.circleDiameter / 2
let circle = UIBezierPath(ovalIn: CGRect(origin: point, size: CGSize(width: Constants.circleDiameter, height: Constants.circleDiameter)))
circle.fill()
}
//Draw horizontal graph lines on the top of everything
let linePath = UIBezierPath()
//top line
linePath.move(to: CGPoint(x:margin, y: topBorder))
linePath.addLine(to: CGPoint(x: width - margin, y:topBorder))
//center line
linePath.move(to: CGPoint(x:margin, y: graphHeight/2 + topBorder))
linePath.addLine(to: CGPoint(x:width - margin, y:graphHeight/2 + topBorder))
//bottom line
linePath.move(to: CGPoint(x:margin, y:height - bottomBorder))
linePath.addLine(to: CGPoint(x:width - margin, y:height - bottomBorder))
let color = UIColor(white: 1.0, alpha: Constants.colorAlpha)
color.setStroke()
linePath.lineWidth = 1.0
linePath.stroke()
}
}
I am trying to mark the points accordingly with the user input - see below:
func budgetAvailableCalculationFunction() {
let bankValue = (userSalary as NSString).integerValue
let bankPorcentage: Int = 100
let expenses = (userExpenses as NSString).integerValue
let calculation1: Int = expenses * bankPorcentage
let calculation2: Int = calculation1 / bankValue
let cashPorcentageAvailable = calculation2
let value: [Int] = [expenses]
self.setupGraphy(points: value)
progressView.progress = 0.0
progress.completedUnitCount = Int64(cashPorcentageAvailable)
progressView.setProgress(Float(self.progress.fractionCompleted), animated: true)
progressView.progressTintColor = UIColor.rgb(red: 239, green: 75, blue: 92)
progressView.backgroundColor = UIColor.rgb(red: 239, green: 75, blue: 92)
progressView.trackTintColor = .white
progressView.clipsToBounds = false
progressView.translatesAutoresizingMaskIntoConstraints = false
progressView.layer.cornerRadius = 0
porcentageLabelForMonth.text = "\(Int(self.progress.fractionCompleted * 100)) %"
}
The setupGraphy is just a function that returns an array of indexes that the user adds it.
Please note that using the same function at the viewDidLoad works:
self.setupGraphy(points: [100, 400, 200, 250])
enter image description here
Anyone?
Code for viewDidLoad:
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.rgb(red: 245, green: 246, blue: 250)
tableView.delegate = self
tableView.dataSource = self
tableView.register(LastTransactionsCell.self, forCellReuseIdentifier: "LastTransactionsCell")
tableView.backgroundColor = .clear
let now = Date()
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy - LLLL"
let nameOfMonth = dateFormatter.string(from: now)
currentMonthLabel.text = nameOfMonth
setupUIXViews()
fetchUserInfo()
//static data for now
*self.setupGraphy(points: [100, 400, 200, 250])*
DispatchQueue.main.asyncAfter(deadline: .now() + 2){
self.budgetAvailableCalculationFunction()
}
}
The way I managed to fix (seems like a temporary fix) is by creating a new Int variable at the top of the project and passing the fetch data from the DB to these variables. That way I can use them at viewDidLoad instead of inside the fetch function.

Show a video with transparent background on ARKit

I am trying to simply show a video removing part of its background which is meant to be transparent. I've tried several approaches courtesy of Stack Overflow, and so far they are all sub-par. The one that came closest to the results I'm seeking is the one in this link: ARKit / SpriteKit - set pixelBufferAttributes to SKVideoNode or make transparent pixels in video (chroma-key effect) another way
However, as sensible as the approach is, and as much as it seems to be working for the person who asked the question, in my case it only turns the whole video white.
My strategy is to show the video and add the effect as follows:
func setVideoNode(named name: String, in node: SCNNode, with imageReference: ARReferenceImage?, size: CGSize = CGSize(width: 500, height: 320), extension ext: String = "mp4") {
let nodeWidth = imageReference!.physicalSize.width
let nodeHeigth = imageReference!.physicalSize.height
guard let videoUrl = Bundle.main.url(forResource: name, withExtension: ext) else {
print("Guard Fail")
return
}
self.currentPlayer = AVPlayer(url: videoUrl)
let videoNode = SKVideoNode(avPlayer: self.currentPlayer)
videoNode.size = size
videoNode.name = name
videoNode.yScale = -1.0
videoNode.play()
let effectNode = SKEffectNode()
effectNode.addChild(videoNode)
effectNode.filter = colorCubeFilterForChromaKey(hueAngle: 0)
let planeGeometry = SCNPlane(width: nodeWidth, height: nodeHeigth)
planeGeometry.firstMaterial?.diffuse.contents = effectNode
planeGeometry.firstMaterial?.isDoubleSided = true
let planeNode = SCNNode()
planeNode.geometry = planeGeometry
planeNode.position = SCNVector3(planeNode.position.x + 1, 0.5, 0)
planeNode.eulerAngles.x = -.pi / 2
node.addChildNode(planeNode)
self.currentPlayer.play()
}
func RGBtoHSV(r : Float, g : Float, b : Float) -> (h : Float, s : Float, v : Float) {
var h : CGFloat = 0
var s : CGFloat = 0
var v : CGFloat = 0
let col = UIColor(red: CGFloat(r), green: CGFloat(g), blue: CGFloat(b), alpha: 1.0)
col.getHue(&h, saturation: &s, brightness: &v, alpha: nil)
return (Float(h), Float(s), Float(v))
}
func colorCubeFilterForChromaKey(hueAngle: Float) -> CIFilter {
let hueRange: Float = 20 // degrees size pie shape that we want to replace
let minHueAngle: Float = (hueAngle - hueRange/2.0) / 360
let maxHueAngle: Float = (hueAngle + hueRange/2.0) / 360
let size = 64
var cubeData = [Float](repeating: 0, count: size * size * size * 4)
var rgb: [Float] = [0, 0, 0]
var hsv: (h : Float, s : Float, v : Float)
var offset = 0
for z in 0 ..< size {
rgb[2] = Float(z) / Float(size) // blue value
for y in 0 ..< size {
rgb[1] = Float(y) / Float(size) // green value
for x in 0 ..< size {
rgb[0] = Float(x) / Float(size) // red value
hsv = RGBtoHSV(r: rgb[0], g: rgb[1], b: rgb[2])
// TODO: Check if hsv.s > 0.5 is really nesseccary
let alpha: Float = (hsv.h > minHueAngle && hsv.h < maxHueAngle && hsv.s > 0.5) ? 0 : 1.0
cubeData[offset] = rgb[0] * alpha
cubeData[offset + 1] = rgb[1] * alpha
cubeData[offset + 2] = rgb[2] * alpha
cubeData[offset + 3] = alpha
offset += 4
}
}
}
let b = cubeData.withUnsafeBufferPointer { Data(buffer: $0) }
let data = b as NSData
let colorCube = CIFilter(name: "CIColorCube", parameters: [
"inputCubeDimension": size,
"inputCubeData": data
])
return colorCube!
}
In my case, I'm trying to remove reds. Is there any other way to achieve this?
Hi dear friend as you seen in this link
.You must be use no between 330 to 360 degree for remove red color.
effectNode.filter = colorCubeFilterForChromaKey(hueAngle: 330)

Get size of dae node

Suppose I have a collada file which has a box in it and I import the dae file into the scene. Now after importing in the scene I know the dae object is a box. How can I get the dimensions of the box in scenekit after adding it to the scene
If I import the node as a SCNBox i get errors saying that SNCBox is not a subtype of SCNNode.
floorNode = scene.rootNode.childNodeWithName("floor", recursively: false) as SCNBox
floorNode?.physicsBody = SCNPhysicsBody.staticBody()
floorNode?.physicsBody?.categoryBitMask = 2
let floorGeo: SCNBox = floorNode.geometry! as SCNBox
How do I get the dimensions if SCNNode is the only way to import nodes?
SCNBox is just helper subclass of SCNGeometry for box geometry creation. When you import Collada into a scene, you get scene graph of SCNNodes with SCNGeometries, not SCNBox'es or SCNCones etc doesn't matter how they look. If you want to get dimensions of any node you should use SCNBoundingVolume protocol, which is implemented by both SCNNode and SCNGeometry classes:
func getBoundingBoxMin(_ min: UnsafeMutablePointer,
max max: UnsafeMutablePointer) -> Bool
With this method you will get bounding box corners.
For box-shaped geometry, dimensions will match bounding box.
Example:
var v1 = SCNVector3(x:0, y:0, z:0)
var v2 = SCNVector3(x:0, y:0, z:0)
node.getBoundingBoxMin(&v1, max:&v2)
Where node is node you want to get bounding box of.
Results will be in v1 and v2.
Swift 3
Using Swift 3 you can simply use node.boundingBox.min and node.boundingBox.max respectively.
Example Swift code on how to use boundingBox:
var min = shipNode.boundingBox.min
var max = shipNode.boundingBox.max
let w = CGFloat(max.x - min.x)
let h = CGFloat(max.y - min.y)
let l = CGFloat(max.z - min.z)
let boxShape = SCNBox (width: w , height: h , length: l, chamferRadius: 0.0)
let shape = SCNPhysicsShape(geometry: boxShape, options: nil)
shipNode.physicsBody!.physicsShape = SCNPhysicsShape(geometry: boxShape, options: nil)
shipNode.physicsBody = SCNPhysicsBody.dynamic()
Swift 5.4.
import ARKit
extension SCNNode {
var height: CGFloat { CGFloat(self.boundingBox.max.y - self.boundingBox.min.y) }
var width: CGFloat { CGFloat(self.boundingBox.max.x - self.boundingBox.min.x) }
var length: CGFloat { CGFloat(self.boundingBox.max.z - self.boundingBox.min.z) }
var halfCGHeight: CGFloat { height / 2.0 }
var halfHeight: Float { Float(height / 2.0) }
var halfScaledHeight: Float { halfHeight * self.scale.y }
}
// Usage:
let node = SCNNode()
node.height
node.width
node.lenght

when dynamic body falls down, its `_ball.position.y` value doesn't change

I have pretty simple question -
func setupScene() {
let sceneView = view as SCNView
sceneView.backgroundColor = SKColor.blackColor()
_scene = SCNScene()
sceneView.scene = _scene
sceneView.showsStatistics = true
// Scene gravity
sceneView.scene?.physicsWorld.gravity = SCNVector3Make(0, -70, 0)
sceneView.scene?.physicsWorld.speed = 2.0
sceneView.delegate = self
}
At some point of time have spawnBall method:
func spawnBall() {
_ball = SCNNode(geometry: SCNSphere(radius: 15))
_ball.geometry?.firstMaterial?.diffuse.contents = UIImage(named: "ball")
_ball.geometry?.firstMaterial?.emission.contents = UIImage(named: "ball")
_ball.geometry?.firstMaterial?.emission.intensity = 0
_ball.physicsBody = SCNPhysicsBody(type: SCNPhysicsBodyType.Dynamic, shape: nil)
_ball.physicsBody?.restitution = 0.1
_ball.physicsBody?.angularVelocity = SCNVector4(x: 1, y: 1, z: 1, w: 1)
let position = SCNVector3Make(0, 15*10, -200)
_ball.position = position
_scene.rootNode.addChildNode(_ball)
}
The question is - why when dynamic body falls down, the it's _ball.position.y value doesn't change?
I used to UIKit when you move UIView - it's origin changes (I was assuming to see something similar, coz I set _ball.position.y = 150 and it should go down)
And how to observe it's movement?
the position of its presentationNode will change. The model values are left untouched by the physics engine.

Resources