Positioning a box in an AR scene view with the same coordinates as an AR plane - position

I am trying to position a box in an AR view and I need it to be at the same position as my AR Plane, or where the Plane Anchor is detected. My code is trying to call to the addBoxEToPlane func so that I can position BoxE the same as the plane, but it gives me the error "Value of tuple type '()' has no member 'position' ". If anybody can help me call to that function, or basically position that box the same as the AR Plane, I would really appreciate it!
func addBoxEToPlane(){
let boxE = SCNNode(geometry: SCNBox(width: 2.0, height: 2.0, length: 2.0, chamferRadius: 0))
boxE.geometry?.firstMaterial?.diffuse.contents = UIColor.red
}
func updateWith(anchor: ARPlaneAnchor) {
plane.width = CGFloat(anchor.extent.x)
plane.height = CGFloat(anchor.extent.z)
position = SCNVector3Make(anchor.center.x, 0, anchor.center.z)
if let grid = plane.materials.first as? GridMaterial {
grid.updateWith(anchor: anchor)
}
let XCoord = (plane.width)/2
let YCoord = (plane.height)/2
addBoxEToPlane().position = SCNVector3(XCoord, YCoord, anchor.center.z)
}

Related

Calculating points of a quadrilateral to emulate fluid in a container (in Swift)

I am trying to create a FluidView shape in SwiftUI which acts like a fluid in a container, such that when the device is at a particular angle, so too is the shape / 'fluid'. The shape also has a specific capacity, percentFilled, which indicates how much of the parent's view should be filled.
Using these constraints, the invariant for the class is
lines.area == rect.area * percentFilled
where lines is the quadrilateral and rect is the bounding rectangle. This invariant implies that the 'volume' of the shape remains constant for each percentFilled irrespective of the tilt angle.
Here is what I have so far:
/// A View made using a specified angle and amount to fill
/// - Invariant: The area of the view is exactly equal to the area of the rectangle of the parent view times `percentFilled`
struct FluidView: Shape {
var angle: CGFloat = 0.0
var percentFilled: CGFloat = 0
/// Creates a new FluidView
/// - Parameters:
/// - angle: A value in the range `0...1`. A value of `0` indicates the view is horizontal, and an angle of `1` indicates the view is vertical (horizontal if viewed as landscape)
/// - percentFilled: the amount of the view bounds to fill represented as a value in the range `0...1`. A value of `x` indicates that `x * 100`% of the parent view is covered by this view
init(angle: CGFloat = 0, percentFilled: CGFloat = 0) {
precondition(0...1 ~= angle)
precondition(0...1 ~= percentFilled)
self.angle = angle
self.percentFilled = percentFilled
}
func path(in rect: CGRect) -> Path {
var path = Path()
path.move(to: CGPoint(x: 0, y: rect.height * (1 - percentFilled))) // top left
let lines = [
(0, rect.height ), // bottom left
(rect.width * 1 / (1 + angle - percentFilled), rect.height ), // bottom right
(rect.width * 1 / (1 + angle - percentFilled), rect.height * (1 + angle - percentFilled)), // top right
(0, rect.height * (1 - angle - percentFilled)) // top left
].map { x, y in
// make sure no points exceed the bounds
CGPoint(x: min(rect.width, x), y: min(rect.height, y))
}
// invariant
assert(lines.area == rect.area * percentFilled)
path.addLines(lines)
return path
}
}
I feel like what I have currently is somewhat close to the goal, however the invariant fails. I believe that my y-coordinates are correct, however I think my calculations for the x-coordinates have to change, but I'm not sure to what they should change.
Any help would be really appreciated, thanks!
Try something like this,
struct FilledShape<S: Shape>: View {
let shape: S
#State var angle: Angle = .zero
#State var percentFull: CGFloat
var gradient: Gradient {
Gradient(stops: [Gradient.Stop(color: .red, location: 0),
Gradient.Stop(color: .red, location: percentFull),
Gradient.Stop(color: .blue, location: percentFull),
Gradient.Stop(color: .blue, location: 1)])
}
var body: some View {
shape.rotation(angle)
.fill(LinearGradient(gradient: gradient, startPoint: .bottom, endPoint: .top))
}
}
struct ContentView: View {
#State var angle: Angle = .degrees(30)
var body: some View {
FilledShape(shape: Rectangle(), angle: angle, percentFull: 0.3).frame(width: 100, height: 100)
}
}
Thing is, percent full is really the percent up along the y axis, not the percent of the area filled. You could use some kind of numeric method with GeometryReader to get the area and read the y-value at the appropriate filled area sum (or if you just use quadrilaterals it's easier). By brute force:
extension Shape {
func area(in box: CGRect) -> Int {
var area = 0
for x in 0..<Int(box.width) {
for y in 0..<Int(box.height) {
let point = CGPoint(x: x, y: y)
if self.contains(point) {
area += 1
}
}
}
return area
}
}
As a different approach, look into SpriteKit and SKPhysicsBody.

Phaser 3 - Fix a shape into the camera view

I am trying to draw a minimap from a randomly generated matrix that represents my level.
To do so, I am drawing black or white little squares one by one to represent the matrix visually (I don't know if it the best way to do that with phaser, actually, I am a beginner with this framework).
The map draws correctly but its position is bound to the world not to camera, so when I move it is not visible anymore.
Here is the code I use to draw the map:
generate() {
let wallsGraphics = this._scene.add.graphics({fillStyle : {color : LabyrinthConfig.MAPS.MINI_MAP.WALLS_COLOR}});
let pathGraphics = this._scene.add.graphics({fillStyle : {color : LabyrinthConfig.MAPS.MINI_MAP.PATH_COLOR}});
// Draw the map
let y = 0;
for (let line of this._matrix) {
let x = 0;
for (let cell of line) {
let rect = new Phaser.Geom.Rectangle();
rect.width = LabyrinthConfig.MAPS.MINI_MAP.CELL_WIDTH;
rect.height = LabyrinthConfig.MAPS.MINI_MAP.CELL_HEIGHT;
rect.x = LabyrinthConfig.MAPS.MINI_MAP.POSITION_X + x * LabyrinthConfig.MAPS.MINI_MAP.CELL_WIDTH;
rect.y = LabyrinthConfig.MAPS.MINI_MAP.POSITION_Y + y * LabyrinthConfig.MAPS.MINI_MAP.CELL_HEIGHT;
cell === 0 ? wallsGraphics.fillRectShape(rect) : pathGraphics.fillRectShape(rect);
x++;
}
y++;
}
}
Any help on how to fix this map to the camera view ?
Set scroll factor of your graphics objects to 0.
wallsGraphics.setScrollFactor(0);
pathGraphics.setScrollFactor(0);

How do I position things properly in Conrod

I'm trying to write code using conrod with a winit/glium backend. Conrod is an intermediate mode graphical user interface library for Rust while wininit provides an event model and glium the OpenGL bindings.
My code draws a grid of values onto a screen with a (row,col), i.e. "text1" at (0,0), "text2", (1,0), "text3" at (0,1) etc. where (row,col) is translated to an absolute (x,y) coordinate.
Basic flow is this:
For each value I want to render as text
Calculate the column / row of the value, i.e. (idx % num_cols, idx / num_cols)
Calculate an x and y from the column and row. i.e. (col * WIDTH, ROW * HEIGHT).
Create a widget::Text at (x, y) containing the value
The code that does this is shown below in a slightly simplified form. The entire file is online here.
This should be simple code to implement but when I run it, the text boxes are not drawn from x=0, y=0 but instead somewhere but not exactly offset from the centre of the view in both axes.
As far as I can tell this shouldn't happen because I am explicitly setting the absolute position and have confirmed the coordinates are correct. So I would expect them to render from the top left of the screen. I don't understand what causes the offset.
Any ideas?
The code that calculates position is this:
fn draw_ui(ui: &mut Ui, model: &mut UiModel) {
let ui = &mut ui.set_widgets();
// Canvas is the backdrop to the view
widget::Canvas::new()
.color(BACKGROUND_COLOUR)
.set(model.static_ids.canvas, ui);
// Create text widgets for each value
let state = model.session_state.read().unwrap();
// Create / update the widgets
if state.values.is_empty() {
// ... removed
} else {
let num_cols: usize = 2;
state.values.iter().enumerate().for_each(|(i, v)| {
// Create / update the cell and its state
let (node_id, value) = v;
if let Some(id) = model.value_ids.get(node_id) {
let (col, row) = (i % num_cols, i / num_cols);
let valid = value.is_valid();
let value = if let Some(ref value) = value.value {
value.to_string()
} else {
"None".to_string()
};
// Turn the value into a string to render it
let (x, y) = (col as f64 * (CELL_WIDTH + PADDING), row as f64 * (CELL_HEIGHT + PADDING));
value_widget(&value, valid, x, y, CELL_WIDTH, CELL_HEIGHT, model.static_ids.canvas)
.set(*id, ui);
}
});
}
}
fn value_widget(value: &str, valid: bool, x: f64, y: f64, w: f64, h: f64, canvas_id: conrod::widget::Id) -> widget::Text {
let color = if valid { GOOD_COLOUR } else { BAD_COLOUR };
widget::Text::new(value)
.x_y(x, y)
.w(w).h(h)
.color(color)
}
I think the 0,0 position is in the center of the window. The slight offset you are seeing comes from the text box position specifying the corner of the text box. To get something in the top left of the window you would want to do something like x_position - 0.5 * window_width + x_margin (similar for y).

SpriteKit How to add laser beam and resize it at collision point

I'm developing my first game with SpriteKit and I need to shoot a laser beam. I don't know how to do this since I don't know the size of the laser sprite, does it have to be with size of the screen height and crop the image when a collision is detected? can anyone point me to the right directions please? Have no idea about this XD
Thanks for your comments :D
This could be done by line-of-sight detection system described here in the section Searching for physics bodies :
Useful method would be enumerateBodiesAlongRayStart:end:usingBlock: from SKPhysicsWorld class which enumerates all the physics bodies in the scene that intersect a ray.
Basically you have to set start point and search for end point using the method above.When you know where is the intersection point(end point of laser beam) you can easily draw it.
This is a way late response, but I've got a really nice solution. Here's what it looks like (Swift 3):
In my code I'm calling this when I rotate the node I want the laser to shoot out of:
self.laser = SKShapeNode()
laser.lineWidth = 6
laser.glowWidth = 8
laser.strokeColor = .red
let _ = isTargetVisibleAtAngle(startPoint: startPoint, angle: selectedBeam!.zRotation + (CGFloat.pi / 2), distance: frame.size.height)
And this is the method. Obviously you put in whatever angle you want. The "foundOne" thing is so that it stops on the first object if that ray crosses through multiple targets
func isTargetVisibleAtAngle(startPoint: CGPoint, angle: CGFloat, distance: CGFloat) -> Bool {
let rayStart = startPoint
let rayEnd = CGPoint(x: rayStart.x + distance * cos(angle),
y: rayStart.y + distance * sin(angle))
let path = CGMutablePath()
path.move(to: rayStart)
path.addLine(to: rayEnd)
laser.path = path
var foundOne = false
let _ = physicsWorld.enumerateBodies(alongRayStart: rayStart, end: rayEnd) { (body, point, vector, stop) in
if !foundOne {
foundOne = true
let p = CGMutablePath()
p.move(to: rayStart)
p.addLine(to: point)
self.laser.path = p
}
}
return false
}

Surface 2.0 ScatterViewItem.Center load item and check if items on same position

I've got a problem:
I capture the position of ScatterViewItems: Center.X, Center.Y and Orientation.
For example:
Item1: X: 595,037655575406 Y: 322,207060644012 Orientation: 0,660569393375486
Item2: X: 606,055258773819 Y: 327,601041845081 Orientation: 180,591312945756
If I load the items via code the position is not the same (it seems to be moved a few pixels).
The next issue is: I want to check if two items are exactly on the same position.
If true raise an event and lock the items.
Thank you in advance.
Use ActualCenter property and ActualOrientation property when reading it back.
The Center is moved? Check the margin of the ScatterView
Same Position?
Vector v = item1.ActualCenter - item2.ActualCenter;
if (v.Length < 10) //within 10 pixel distance
{
//Raise your event
item1.CanMove = item2.CanMove = item2.CanRotate = item2.CanRotate = false;
}

Resources