Detecting a change in a compound property - properties

Objective-C would not allow you to run the following code:
myShape.origin.x = 50
This made it easy to detect changes in the origin, since someone using your class was forced to write myShape.origin = newOrigin, and thus you could easily tie in to the setter of this property.
Swift now allows you to perform the original, formerly-disallowed code. Assuming the following class structure, how would you detect the change to the origin in order to execute your own code (e.g. to update the screen)?
struct Point {
var x = 0
var y = 0
}
class Shape {
var origin: Point = Point()
}
Update: Perhaps I should have been more explicit, but assume I don't want to modify the Point struct. The reason is that Shape is but one class that uses Point, there may very well be hundreds of others, not to mention that the origin is not the only way a Point may be used.

Property observers (willSet and didSet) do fire when sub-properties of that property are changed. In this case, when the x or y values of the Point structure change, that property will be set.
Here is my example playground code:
struct Point : Printable
{
var x = 0
var y = 0
var description : String {
{
return "(\(x), \(y))";
}
}
class Shape
{
var origin : Point = Point()
{
willSet(newOrigin)
{
println("Changing origin to \(newOrigin.description)!")
}
}
}
let circle = Shape()
circle.origin.x = 42
circle.origin.y = 77
And here is the console output:
Changing origin to (42, 0)!
Changing origin to (42, 77)!

Doesn't this work?
class Shape {
var origin: Point {
willSet(aNewValueForOrigin) {
// pre flight code
}
didSet(theOldValueOfOrigin) {
// post flight code
}
}
}
Edit: revisited code and added name of arguments to reflect what to expect.

You can use Property Observers also works for structs
Link to the part on the ebook
class StepCounter {
var totalSteps: Int = 0 {
willSet(newTotalSteps) {
println("About to set totalSteps to \(newTotalSteps)")
}
didSet {
if totalSteps > oldValue {
println("Added \(totalSteps - oldValue) steps")
}
}
}
}
let stepCounter = StepCounter()
stepCounter.totalSteps = 200
// About to set totalSteps to 200
// Added 200 steps
stepCounter.totalSteps = 360
// About to set totalSteps to 360
// Added 160 steps
stepCounter.totalSteps = 896
// About to set totalSteps to 896
// Added 536 steps

Use didSet, e.g.,
struct Point {
var x = 0
var y: Int = 0 {
didSet {
println("blah blah")
}
}
}
class Shape {
var origin: Point = Point()
}
let s = Shape()
s.origin.y = 2

Related

Map within a map in terraform variables

Does anyone know if it's possible with possibly code snipits representing whether I can create a map variable within a map variable in terraform variables?
variable "var" {
type = map
default = {
firstchoice = {
firstAChoice ="foo"
firstBChoice = "bar"
}
secondchoice = {
secondAChoice = "foobar"
secondBChoice = "barfoo"
}
}
}
If anyone has any insight to whether this is possible or any documentation that elaborates that would be great.
Yes, it's possible to have map variable as value of map variable key. Your variable just needed right indentation. Also I am putting ways to access that variable.
variable "var" {
default = {
firstchoice = {
firstAChoice = "foo"
firstBChoice = "bar"
}
secondchoice = {
secondAChoice = "foobar"
secondBChoice = "barfoo"
}
}
}
To access entire map value of a map key firstchoice, you can try following
value = "${var.var["firstchoice"]}"
output:
{
firstAChoice = foo
firstBChoice = bar
}
To access specific key of that map key (example firstAChoice), you can try
value = "${lookup(var.var["firstchoice"],"firstAChoice")}"
output: foo
Would this syntax be possible? ${var.var[firstchoice[firstAchoice]]}
With Terraform 0.12+ nested blocks are supported seamlessly. Extending #Avichal Badaya's answer to explain it using an example:
# Nested Variable
variable "test" {
default = {
firstchoice = {
firstAChoice = "foo"
firstBChoice = "bar"
}
secondchoice = {
secondAChoice = "foobar"
secondBChoice = "barfoo"
}
thirdchoice = {
thirdAChoice = {
thirdBChoice = {
thirdKey = "thirdValue"
}
}
}
}
}
# Outputs
output "firstchoice" {
value = var.test["firstchoice"]
}
output "FirstAChoice" {
value = var.test["firstchoice"]["firstAChoice"]
}
output "thirdKey" {
value = var.test["thirdchoice"]["thirdAChoice"]["thirdBChoice"]["thirdKey"]
}
Applying the above, you can verify that Terraform map nesting is now quite powerful and this makes a lot of things easier.
# Apply complete! Resources: 0 added, 0 changed, 0 destroyed.
# Outputs:
firstchoice = {
"firstAChoice" = "foo"
"firstBChoice" = "bar"
}
thirdKey = thirdValue
For more complex structures and Rich Value Types, see HashiCorp Terraform 0.12 Preview: Rich Value Types

Enumerating pairs of elements in a Set (Swift 3)

The Swift 3 IteratorProtocol documentation says : "Copying an iterator is safe, but advancing one copy of an iterator by calling its next() method may invalidate other copies of that iterator."
So, this might not work:
var i = set.makeIterator()
while let obji = i.next() {
var j = i
while let objj = j.next() { // Possible error : might invalidate i
...some code...
}
}
So, I wrote this:
var objects = [Object](set)
for i in 0..<objects.count {
for j in (i+1)..<objects.count {
...some code...
}
}
But isn't there a way to do the same thing without copying the set in an array?

Background Over-riding MousePressed() Ellipse in Draw Function

I've made a particle system in P5.js that explodes particles on mousepressed(). However, I'd also like expanding circles to output on mousepressed(). I was able to draw a circle on mousepressed(), however it seems to be under the background. I am having a brain fart on this. Why isn't the circle appearing along with the particles, above the black background? Thanks for help!
var lifeConstant = 50000;
var startVelMin = -10;
var startVelMax = 7;
var drag = -50;
var planetArray = [];
var planet;
var planet0;
var planet1;
var planet3;
//var planets = ['planet1.gif', 'planet2.gif', 'planet3.gif', 'planet4.gif']// for loop to loop through image files
//for (var i = 0; i < planets.length; i++) { //
function preload(){
//for (var i = 0; i < planetArray.length; i++) {
planet = loadImage('Assets/planet2.gif');
append(planetArray, planet);
planet3 = loadImage('Assets/planet3.gif');
append(planetArray, planet3);
planet0 = loadImage('Assets/planet4.gif');
append(planetArray, planet0);
planet1 = loadImage('Assets/planet1.gif');
append(planetArray, planet1);
}
//planetArray.add(planet);
function setup() {
createCanvas(windowWidth, windowHeight);
systems = [];
background(51);
}
function draw() {
background(0)
for (i = 0; i < systems.length; i++) {
systems[i].run();
systems[i].addParticle();
}
if (systems.length==0) {
fill(255);
textAlign(CENTER);
textSize(42);
text("Click mouse to create Big Bangs", width/2, height/2);
}
}
function mousePressed() {
this.p = new ParticleSystem(createVector(mouseX, mouseY));
systems.push(p);
fill(230,120, 0);
ellipse(mouseX, mouseY, 100, 100);
}
// A simple Particle class
var Particle = function(position) {
this.acceleration = createVector(0, .1);
this.velocity = createVector(random(startVelMin,startVelMax), random(startVelMin,startVelMax));
this.position = position.copy();
this.lifespan = lifeConstant;
};
Particle.prototype.run = function() {
this.update();
this.display();
};
// Method to update position
Particle.prototype.update = function(){
this.velocity.add(drag*this.acceleration);
this.position.add(this.velocity);
this.lifespan -= 150;
};
// Method to display
Particle.prototype.display = function () {
//fill(random(255), random(255), random(200));
//stroke(20, this.lifespan);
//strokeWeight(1);
//fill(random(255),this.lifespan);
//ellipse(this.position.x, this.position.y, 15, 15);
image(planetArray[floor(random(4))], this.position.x, this.position.y, 15, 15);
};
// Is the particle still useful?
Particle.prototype.isDead = function () {
if (this.lifespan < 0) {
return true;
} else {
return false;
}
};
var ParticleSystem = function (position) {
this.origin = position.copy();
this.particles = [];
};
ParticleSystem.prototype.addParticle = function () {
// Add either a Particle or CrazyParticle to the system
if (int(random(0, 2)) == 0) {
p = new Particle(this.origin);
}
else {
p = new
CrazyParticle(this.origin);
}
this.particles.push(p);
};
ParticleSystem.prototype.run = function () {
for (var i = this.particles.length - 1; i >= 0; i--) {
var p = this.particles[i];
p.run();
if (p.isDead()) {
this.particles.splice(i, 1);
}
}
};
// A subclass of Particle
function CrazyParticle(origin) {
// Call the parent constructor, making sure (using Function#call)
// that "this" is set correctly during the call
Particle.call(this, origin);
// Initialize our added properties
this.theta = 0.0;
};
// Create a Crazy.prototype object that inherits from Particle.prototype.
// Note: A common error here is to use "new Particle()" to create the
// Crazy.prototype. That's incorrect for several reasons, not least
// that we don't have anything to give Particle for the "origin"
// argument. The correct place to call Particle is above, where we call
// it from Crazy.
CrazyParticle.prototype = Object.create(Particle.prototype); // See note below
// Set the "constructor" property to refer to CrazyParticle
CrazyParticle.prototype.constructor = CrazyParticle;
// Notice we don't have the method run() here; it is inherited from Particle
// This update() method overrides the parent class update() method
CrazyParticle.prototype.update=function() {
Particle.prototype.update.call(this);
// Increment rotation based on horizontal velocity
this.theta += (this.velocity.x * this.velocity.mag()) / 10.0;
}
// This display() method overrides the parent class display() method
CrazyParticle.prototype.display=function() {
// Render the ellipse just like in a regular particle
// Particle.prototype.display.call(this);
}
The mousePressed() function is called whenever the mouse button is pressed down. The draw() function is called 60 times per second. So anything you draw in the mousePressed() function will almost immediately be drawn over with whatever you draw in the draw() function.
You need to have all of your drawing code called from your draw() function. You could do this by using a variable that keeps track of whether the mouse is pressed. Luckily, p5.js already has a variable that does exactly that: mouseIsPressed

My current GPS Position under Nutiteq is not properly updated

as basis for my GPS functionality I've taken HelloMap3D Example of Nutiteq (Thx Jaak) and I adapted to show my current GPS position light different of this example, so, no growing yelow circles but a fix blue translucent circle with a center point as my current Position and works fine except the update. It should erase the past position if location is changed, so that
this update happens as in the example in the method onLocationChanged
This is the code in my Main Activity
protected void initGps(final MyLocationCircle locationCircle) {
final Projection proj = mapView.getLayers().getBaseLayer().getProjection();
locationListener = new LocationListener() {
#Override
public void onLocationChanged(Location location) {
locationCircle.setLocation(proj, location);
locationCircle.setVisible(true);
}
// Another Methods...
}
}
I have adapted MyLocationCircle Class like this
public void update() {
//Draw center with a drawable
Bitmap bitmapPosition = BitmapFactory.decodeResource(activity.getResources(), R.drawable.ic_home);
PointStyle pointStyle = PointStyle.builder().setBitmap(bitmapPosition).setColor(Color.BLUE).build();
// Create/update Point
if ( point == null ) {
point = new Point(circlePos, null, pointStyle, null);
layer.add(point);
} else { // We just have to change the Position to actual Position
point.setMapPos(circlePos);
}
point.setVisible(visible);
// Build closed circle
circleVerts.clear();
for (float tsj = 0; tsj <= 360; tsj += 360 / NR_OF_CIRCLE_VERTS) {
MapPos mapPos = new MapPos(circleScale * Math.cos(tsj * Const.DEG_TO_RAD) + circlePos.x, circleScale * Math.sin(tsj * Const.DEG_TO_RAD) + circlePos.y);
circleVerts.add(mapPos);
}
// Create/update line
if (circle == null) {
LineStyle lineStyle = LineStyle.builder().setWidth(0.05f).setColor(Color.BLUE).build();
PolygonStyle polygonStyle = PolygonStyle.builder().setColor(Color.BLUE & 0x80FFFFFF).setLineStyle(lineStyle).build();//0xE0FFFF
circle = new Polygon(circleVerts, null, polygonStyle, circle_data);
layer.add(circle);
} else {
circle.setVertexList(circleVerts);
}
circle.setVisible(visible);
}
public void setVisible(boolean visible) {
this.visible = visible;
}
public void setLocation(Projection proj, Location location) {
circlePos = proj.fromWgs84(location.getLongitude(), location.getLatitude());
projectionScale = (float) proj.getBounds().getWidth();
circleRadius = location.getAccuracy();
// Here is the most important modification
update();
}
So, each time our Position changes is called onLocationChanged(Location location) Method and there will be called locationCircle.setLocation(location) and last there, it will be called update called.
The questions are, What am I making wrong? and How can I solve it?
Thank you in advance.
You create and add new circle with every update. You should reuse single one, just update vertexes with setVertexList(). In particular this line should be outside onLocationChanged cycle, somewhere in initGPS perhaps:
circle = new Polygon(circleVerts, null, polygonStyle, circle_data);

JScript.NET private variables

I'm wondering about JScript.NET private variables. Please take a look on the following code:
import System;
import System.Windows.Forms;
import System.Drawing;
var jsPDF = function(){
var state = 0;
var beginPage = function(){
state = 2;
out('beginPage');
}
var out = function(text){
if(state == 2){
var st = 3;
}
MessageBox.Show(text + ' ' + state);
}
var addHeader = function(){
out('header');
}
return {
endDocument: function(){
state = 1;
addHeader();
out('endDocument');
},
beginDocument: function(){
beginPage();
}
}
}
var j = new jsPDF();
j.beginDocument();
j.endDocument();
Output:
beginPage 2
header 2
endDocument 2
if I run the same script in any browser, the output is:
beginPage 2
header 1
endDocument 1
Why it is so??
Thanks,
Paul.
Just a guess, but it appears that JScript.NET doesn't support closures the same way as EMCAScript, so the state variable in endDocument() isn't referencing the private member of the outer function, but rather an local variable (undeclared). Odd.
You don't have to use new when calling jsPDF here since you're using a singleton pattern. jsPDF is returning an object literal so even without new you'll have access to the beginPage and endDocument methods. To be perfectly honest I don't know what the specifications call for when using new on a function that returns an object literal so I'm not sure if JScript.NET is getting it wrong or the browser. But for now try either getting rid of the new before jsPDF() or change your function to this:
var jsPDF = function(){
var state = 0;
var beginPage = function(){
state = 2;
out('beginPage');
};
var out = function(text){
if(state == 2){
var st = 3;
}
MessageBox.Show(text + ' ' + state);
};
var addHeader = function(){
out('header');
};
this.endDocument = function(){
state = 1;
addHeader();
out('endDocument');
};
this.beginDocument: function(){
beginPage();
};
}
That will allow you to use the new keyword and create more than one jsPDF object.
I've come across the same problem. In the following code, the closure bound to fun should contain only one variable called result. As the code stands, the variable result in the function with one parameter seems to be different to the result variable in the closure.
If in this function the line
result = [];
is removed, then the result in the line
return result;
refers to the result in the closure.
var fun = function() {
var result = [];
// recursive descent, collects property names of obj
// dummy parameter does nothing
var funAux = function(obj, pathToObj, dummy) {
if (typeof obj === "object") {
for (var propName in obj) {
if (obj.hasOwnProperty(propName)) {
funAux(obj[propName], pathToObj.concat(propName), dummy);
}
}
}
else {
// at leaf property, save path to leaf
result.push(pathToObj);
}
}
return function(obj) {
// remove line below and `result' 3 lines below is `result' in closure
result = []; // does not appear to be bound to `result' above
funAux(obj, [], "dummy");
return result; // if result 2 lines above is set, result is closure is a different variable
};
}();