How can I add stroke thickness to x3dom LineSet - x3dom

I'm trying to add a stroke thickness to a x3dom indexedLineSet. All I could find so far is the x3dom LineProperty. But the proposed linewidthScaleFactor seems to have no effect.
Here is what I've tried so far:
<shape render="true" bboxcenter="0,0,0" bboxsize="-1,-1,-1" ispickable="true">
<lineset vertexcount="5" solid="true" ccw="true" usegeocache="true" lit="true">
<coordinate point="-0.5 0.5 0, 0.5 0.5 0, 0.5 -0.5 0, -0.5 -0.5 0, -0.5 0.5 0"></coordinate>
</lineset>
<appearance sorttype="auto" alphaclipthreshold="0.1">
<material emissivecolor="1 1 0" ambientintensity="0.2" diffusecolor="0.8,0.8,0.8" shininess="0.2" specularcolor="0,0,0">
<lineproperties linewidthscalefactor="3" applied="true" linetype="1"></lineproperties>
</material>
</appearance>
</shape>

The code snippet is correct. The problem is the browser. In my case chrome does not handle the WebGL lineWidth correct. [Issue Demo]

Related

MDLMesh strange buffer layout

I was curious to see the vertex positions of an MDLMesh for a boxWithExtent and I have notices a strange behaviour. When I print out the positions and normals for the first 3 vertices you get the below values. What I find weird is that for the 1st vertex we get 2 0.0s at the end to fit the stride for sims_float4 but weirdly for every subsequent vertex this becomes 1.0 - 0.0. Anyone has any idea why metal does this instead of just filling the last two positions with 0.0s as the first vertex. Thank you 0.5 0.5 0.5 -1.0 -0.0 -0.0 0.0 0.0 0.5 0.5 -0.5 -1.0 -0.0 -0.0 1.0 0.0 0.5 -0.5 0.5 -1.0 -0.0 -0.0 0.0 1.0
This behaviour goes away if I use a custom vertexDescriptor so I'm confused as what's going on here

Animating CAShapeLayer path smoothly

I'm trying to make a Gauge UIView to mimic the following image as close as possible
func gradientBezierPath(percent: CGFloat) -> UIBezierPath {
// vary this to move the start of the arc
let startAngle = CGFloat(180).toRadians()//-CGFloat.pi / 2 // This corresponds to 12 0'clock
// vary this to vary the size of the segment, in per cent
let proportion = CGFloat(50 * percent)
let centre = CGPoint (x: self.frame.size.width / 2, y: self.frame.size.height / 2)
let radius = self.frame.size.height/4//self.frame.size.width / (CGFloat(130).toRadians())
let arc = CGFloat.pi * 2 * proportion / 100 // i.e. the proportion of a full circle
// Start a mutable path
let cPath = UIBezierPath()
// Move to the centre
cPath.move(to: centre)
// Draw a line to the circumference
cPath.addLine(to: CGPoint(x: centre.x + radius * cos(startAngle), y: centre.y + radius * sin(startAngle)))
// NOW draw the arc
cPath.addArc(withCenter: centre, radius: radius, startAngle: startAngle, endAngle: arc + startAngle, clockwise: true)
// Line back to the centre, where we started (or the stroke doesn't work, though the fill does)
cPath.addLine(to: CGPoint(x: centre.x, y: centre.y))
return cPath
}
override func draw(_ rect: CGRect) {
// let endAngle = percent == 1.0 ? 0 : (percent * 180) + 180
path = UIBezierPath(arcCenter: CGPoint(x: self.frame.size.width/2, y: self.frame.size.height/2),
radius: self.frame.size.height/4,
startAngle: CGFloat(180).toRadians(),
endAngle: CGFloat(0).toRadians(),
clockwise: true)
percentPath = UIBezierPath(arcCenter: CGPoint(x: self.frame.size.width/2, y: self.frame.size.height/2),
radius: self.frame.size.height/4,
startAngle: CGFloat(180).toRadians(),
endAngle: CGFloat(0).toRadians(),
clockwise: true)
let shapeLayer = CAShapeLayer()
shapeLayer.path = self.path.cgPath
shapeLayer.strokeColor = UIColor(red: 110 / 255, green: 78 / 255, blue: 165 / 255, alpha: 1.0).cgColor
shapeLayer.fillColor = UIColor.clear.cgColor
shapeLayer.lineWidth = 5.0
shapeLayer.lineCap = .round
self.layer.addSublayer(shapeLayer)
percentLayer.path = self.percentPath.cgPath
percentLayer.strokeColor = UIColor(red: 255 / 255, green: 93 / 255, blue: 41 / 255, alpha: 1.0).cgColor
percentLayer.fillColor = UIColor.clear.cgColor
percentLayer.lineWidth = 8.0
// percentLayer.strokeEnd = CGFloat(percent)
percentLayer.lineCap = .round
self.layer.addSublayer(percentLayer)
// n.b. as #MartinR points out `cPath.close()` does the same!
// circle shape
circleShape.path = gradientBezierPath(percent: 1.0).cgPath//cPath.cgPath
circleShape.strokeColor = UIColor.clear.cgColor
circleShape.fillColor = UIColor.green.cgColor
self.layer.addSublayer(circleShape)
gradient.frame = frame
gradient.mask = circleShape
gradient.type = .radial
gradient.colors = [UIColor(red: 255 / 255, green: 93 / 255, blue: 41 / 255, alpha: 0.0).cgColor,
UIColor(red: 255 / 255, green: 93 / 255, blue: 41 / 255, alpha: 0.0).cgColor,
UIColor(red: 255 / 255, green: 93 / 255, blue: 41 / 255, alpha: 0.4).cgColor]
gradient.locations = [0, 0.35, 1]
gradient.startPoint = CGPoint(x: 0.49, y: 0.55) // increase Y adds more orange from top to bottom
gradient.endPoint = CGPoint(x: 0.98, y: 1) // increase x pushes orange out more to edges
self.layer.addSublayer(gradient)
//myTextLayer.string = "\(Int(percent * 100))"
myTextLayer.backgroundColor = UIColor.clear.cgColor
myTextLayer.foregroundColor = UIColor.white.cgColor
myTextLayer.fontSize = 85.0
myTextLayer.frame = CGRect(x: (self.frame.size.width / 2) - (self.frame.size.width/8), y: (self.frame.size.height / 2) - self.frame.size.height/8, width: 120, height: 120)
self.layer.addSublayer(myTextLayer)
}
This produces the following in a playground which is pretty close to what i'm aiming for:
The problem comes when trying to animate the change in the gauge value. I can animate the percentLayer pretty easy with modifying strokeEnd, but animating the circleShape.path for the gradient results in some non-smooth animations if there's a large change in the percent value of the gauge. Here's the function i use to animate both layers (it's called on a timer every 2 seconds right now to simulate gauge value changes).
func randomPercent() {
let random = CGFloat.random(in: 0.0...1.0)
// Animate the percent layer
let animation = CABasicAnimation(keyPath: "strokeEnd")
animation.fromValue = percentLayer.strokeEnd
animation.toValue = random
animation.duration = 1.5
percentLayer.strokeEnd = random
percentLayer.add(animation, forKey: nil)
// Animate the gradient layer
let newShapePath = gradientBezierPath(percent: random)
let gradientAnimation = CABasicAnimation(keyPath: "path")
gradientAnimation.duration = 1.5
gradientAnimation.toValue = newShapePath
gradientAnimation.fromValue = circleShape.path
circleShape.path = newShapePath.cgPath
self.circleShape.add(gradientAnimation, forKey: nil)
myTextLayer.string = "\(Int(random * 100))"
}
Notice how when the animation is done with small changes in the value, the animation looks good. However when there's a large change the gradient animation doesn't look natural at all. Any ideas on how to improve this? Or maybe is it possible to animate a different keyPath for better performance? Any help would be greatly appreciated!
You can't use the Bezier path addArc() function to animate an arc and change the arc distance.
The problem is control points. In order for an animation to work smoothly, the starting and ending shape must have the same number and type of control points. Under the covers, the UIBezierPath (and CGPath) objects create arcs approximating a circle by combining Bezier curves (I don't remember if it uses Quadratic or Cubic Bezier curves.) The entire circle is made up of multiple connected Bezier curves ("Bezier" the mathematical spline function, not UIBeizerPath, which is a UIKit function that creates shapes that can include Bezier paths.) I seem to remember a Bezier approximation of a circle is made up of 4 linked cubic Bezier curves. (See this SO answer for a discussion of what that looks like, if you're interested.)
Here is my understanding of how it works. (I might have the details wrong, but it illustrates the problem in any case.) As you move from <= 1/4 of a full circle to > 1/4 of a full circle, the arc function will use first 1 cubic Bezier section, then 2. At the transition from <= 1/2 of a circle to > 1/2 of a circle, it will shift to 3 Bezier curves, and at the transition from <= 3/4 of a circle to > 3/4 of a circle, it will switch to 4 Bezier curves.
The solution:
You are on the right track with using strokeEnd. Always create your shape as the full circle, and set strokeEnd to something less than 1. That will give you a part of a circle, but in a way that you can animate smoothly. (You can animate strokeStart as well.)
I've animated circles just like you describe using CAShapeLayer and strokeEnd (It was a number of years ago, so it was in Objective-C.) I wrote an article here on OS on using the approach to animate a mask on a UIImageView and create a "clock wipe" animation. If you have an image of your full shaded circle you could use that exact approach here. (You should be able to add a mask layer to any UIView's content layer or other layer, and animate that layer as in my clock wipe demo. Let me know if you need help deciphering the Objective-C.
Here is the sample clock wipe animation I created:
Note that you can use this effect to mask any layer, not just an image view.
EDIT: I posted an update to my clock wipe animation question and answer with a Swift version of the project.
You can get to the new repo directly at https://github.com/DuncanMC/ClockWipeSwift.
For your application I would set up the parts of your gauge that you need to animate as a composite of layers. You'd then attach a CAShapeLayer based mask layer to that composite layer and add a circle arc path to that shape layer and animate the strokeEnd as shown in my sample project. My clock wipe animation reveals the image like the sweep of a clock hand from the center of the layer. In your case you'd center the arc on the bottom center of your layer, and only use a half-circle arc in your shape layer. Using a mask that way would give you a sharp-edged crop to your composited layer. you'd lose the round end caps on your red arc. To fix that you'd have to animate the red arc as it's own shape layer (using strokeEnd) and animate the gradient fill's arc strokeEnd separately.

CGAffineTransform scale with slider

I have a UISlider which goes from 0 to 275. I want to use the slider to scale an UIImageView.
When my slider value is 0 my UIImageView should have the original size (scaleX: 1, scaleY: 1).
When my slider value is 275 my UIImageView should scale to 0.85.
Can someone suggest a good formula to calculate the scale value in relationship with slider value?
Something like this
let scale = slider.value >= 275 ? 0.85 : 1
imageView.transform = CGAffineTransform(scaleX: scale, scaleY: scale)
But I have some trouble making the scale dynamic based on slider value.
You could use a linear scale such as:
let scale = 1.0 - slider.value/275.0 * 0.15

I can rotate an object around y axis in iOS using swift, but how to rotate many objects around a single axis? [duplicate]

This question already has answers here:
how to make objects appear from one side when I rotate them in iOS ? (code + gif ) inside
(2 answers)
Closed 7 years ago.
I have created this function which rotates an object (imageViewLogo) around the Y Axis
the question is, how to rotate many objects around a specific point ?
for example the squares rotate around the blue axis.
func animate()
{
var id = CATransform3DIdentity
id.m34 = -1.0 / 1000
var transformAnim = CAKeyframeAnimation(keyPath:"transform")
transformAnim.values = [
NSValue(CATransform3D: CATransform3DRotate(id, 0 * CGFloat(-M_PI_2), 0, 1.0, 0)),
NSValue(CATransform3D: CATransform3DRotate(id, 1 * CGFloat(-M_PI_2), 0, 1.0, 0)),
NSValue(CATransform3D: CATransform3DRotate(id, 0 * CGFloat(-M_PI_2), 0, 1.0, 0))
]
transformAnim.keyTimes = [0, 0.5, 1.0]
transformAnim.duration = 0.7
self.imageViewLogo.layoutIfNeeded()
self.imageViewLogo.layer.addAnimation(transformAnim, forKey: "transform")
}
The simplest thing to do will be to put all your views that you want to rotate inside another view, and then rotate that superview. (You can also do the same thing with placing multiple layers inside another layer and rotating the containing layer, but that's more work.)

CGContextStrokePath color

I use CGContextStrokePath painted on a straight line in a white background picture, stroke color is red, alpha is 1.0
After drawing the line, why the points is not (255, 0, 0), but (255, 96, 96)
Why not pure red?
Quartz (the iOS drawing layer) uses antialiasing to make things look smooth. That's why you're seeing non-pure-red pixels.
If you stroke a line of width 1.0 and you want only pure red pixels, the line needs to be horizontal or vertical and it needs to run along the center of the pixels, like this:
CGContextMoveToPoint(gc, 0, 10.5);
CGContextAddLineToPoint(gc, 50, 10.5);
CGContextStroke(gc);
The .5 in the y coordinates puts the long along the centers of the pixels.

Resources