Why need get/set when using Computed Properties in Swift, while we can code without them actually? - swift

Here's an example from Official Guide by Apple:
struct​ ​Point​ {
​ ​var​ ​x​ = ​0.0​, ​y​ = ​0.0
​}
​struct​ ​Size​ {
​ ​var​ ​width​ = ​0.0​, ​height​ = ​0.0
​}
​struct​ ​Rect​ {
​ ​var​ ​origin​ = ​Point​()
​ ​var​ ​size​ = ​Size​()
​ ​var​ ​center​: ​Point​ {
​ ​get​ {
​ ​let​ ​centerX​ = ​origin​.​x​ + (​size​.​width​ / ​2​)
​ ​let​ ​centerY​ = ​origin​.​y​ + (​size​.​height​ / ​2​)
​ ​return​ ​Point​(​x​: ​centerX​, ​y​: ​centerY​)
​ }
​ ​set​(​newCenter​) {
​ ​origin​.​x​ = ​newCenter​.​x​ - (​size​.​width​ / ​2​)
​ ​origin​.​y​ = ​newCenter​.​y​ - (​size​.​height​ / ​2​)
​ }
​ }
​}
​var​ ​square​ = ​Rect​(​origin​: ​Point​(​x​: ​0.0​, ​y​: ​0.0​),
​ ​size​: ​Size​(​width​: ​10.0​, ​height​: ​10.0​))
​let​ ​initialSquareCenter​ = ​square​.​center
​square​.​center​ = ​Point​(​x​: ​15.0​, ​y​: ​15.0​)
​println​(​"square.origin is now at (​\(​square​.​origin​.​x​)​, ​\(​square​.​origin​.​y​)​)"​)
​// prints "square.origin is now at (10.0, 10.0)”
This make me pretty confused why we need get/set here, even we actually can do without them, then I tried:
struct anotherRect {
var origin = Point()
var size = Size()
var center: Point{
return Point(x: (origin.x + size.width/2), y: (origin.y + size.height/2))
}
}
var anotherSquare = Rect(origin: Point(x: 20.0, y: 20.0), size: Size(width: 10.0, height: 10.0))
println("anotherSquare.center is (\(anotherSquare.center.x), \(anotherSquare.center.y))")
// prints "anotherSquare.center is (25.0, 25.0)"
anotherSquare.center = Point(x: 30.0, y: 30.0)
println("anotherSquare.origin is now at (\(anotherSquare.origin.x), \(anotherSquare.origin.y))")
// prints "anotherSquare.origin is now at (25.0, 25.0)"
With this, I can do get value or set a new value to the computed properties, exactly the same get/set by Apple's way. Do we really need get/set in our code? and Why?
I'm just a newcomer, so feel free let me know all about this stuff? I really appriciate your help!!

They are not the same. If you don't specify the get set keywords, you only implement the getter.
struct AnotherRect {
var origin = Point()
var size = Size()
var center: Point {
return Point(x: (origin.x + size.width/2), y: (origin.y + size.height/2))
}
}
is equivalent to
struct AnotherRect {
var origin = Point()
var size = Size()
var center: Point {
get {
return Point(x: (origin.x + size.width/2), y: (origin.y + size.height/2))
}
}
}
Your example got you confused because you have an error in your code:
var anotherSquare = Rect(origin: Point(x: 20.0, y: 20.0), size: Size(width: 10.0, height: 10.0))
should be
var anotherSquare = AnotherRect(origin: Point(x: 20.0, y: 20.0), size: Size(width: 10.0, height: 10.0))
Try that and you will notice that you cannot assign to the center property.
Also, the convention is that you name your custom types starting with a capital letter, e.g.: AnotherRect.

Related

How to using flutter implement this html5 canvas animation

I'm learning flutter recently, but I still don't understand the use of flutter canvas,
I hope to get your help, here.
How to using flutter implement this html5 canvas animation?
jsfiddle animation demo link
HTML code
margin: 0;
padding: 0;
background: #000000;
}
canvas {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
</style>
</head>
<body>
<canvas id="mycanvas"></canvas>
<script type="text/javascript">
const mycanvas = document.getElementById('mycanvas')
const context = mycanvas.getContext('2d')
const canvasWidth = window.innerWidth
const canvasHeight = window.innerHeight
mycanvas.width = canvasWidth
mycanvas.height = canvasHeight
console.log(canvasWidth, canvasHeight);
// 创建渐变
function createGradient(context, p0, p1) {
const gradient = context.createLinearGradient(p0.x, p0.y, p1.x, p1.y)
gradient.addColorStop(0, 'rgba(255, 255, 0, 0)')
gradient.addColorStop(1, 'rgba(255, 255, 0, 1)')
return gradient
}
// 绘制曲线
function createCurveLine(points) {
const gradient = createGradient(context, points[0], points[points.length - 1])
context.beginPath()
context.moveTo(points[0].x, points[0].y)
// 参数 points 是曲线上一部分连续点的集合,我们用 lineTo 把这些点连结起来,就近似的得到了曲线
for (let i = 0; i < points.length; i++) {
const p = points[i]
context.lineTo(p.x, p.y)
}
context.moveTo(points[0].x, points[0].y)
context.strokeStyle = gradient
context.lineCap = 'round'
context.lineWidth = 5
context.shadowColor = 'rgba(255, 0, 255, 1)'
context.shadowBlur = 10
context.stroke()
}
const P0 = {
x: 100,
y: canvasHeight / 2
}
const P1 = {
x: canvasWidth / 2,
y: canvasHeight / 2 - 200
}
const P2 = {
x: canvasWidth - 100,
y: canvasHeight / 2
}
let t0 = 0
let t1 = 0
let points = [] // 存储曲线上点的集合
const lineLength = 0.3;
function draw() {
context.clearRect(0, 0, canvasWidth, canvasHeight);
if (t1 < lineLength) {
t0 = 0;
}
if (t0 > 1 - lineLength) {
t1 = 1;
}
const currentPoint = {
x: computeCurvePoint(P0.x, P1.x, P2.x, t1),
y: computeCurvePoint(P0.y, P1.y, P2.y, t1)
}
// 每当 t1 变化时,就将对应的点添加到 points 集合中
points.push(currentPoint)
const len = points.length
context.save()
if (len > 1) {
createCurveLine(points.slice(Math.floor(len * t0), Math.max(Math.ceil(len * t1), 2)))
}
context.restore()
t0 += 0.005
t1 += 0.005
if (t0 > 1 && t1 > 1) {
t0 = 0
t1 = 0
points = []
}
requestAnimationFrame(draw)
}
draw()
/*!
* 计算二次贝塞尔曲线上的点
* #param {Number} p0 起始点
* #param {Number} p1 控制点
* #param {Number} p2 结束点
* #param {Number} t 0-1的集合
* #return {Number} 返回计算后的点
*/
function computeCurvePoint(p0, p1, p2, t) {
return (1 - t) * (1 - t) * p0 + 2 * t * (1 - t) * p1 + t * t * p2
}
function arc(...points) {
points.forEach(p => {
context.beginPath()
context.arc(p.x, p.y, 3, 0, Math.PI * 2)
context.stroke();
});
}
</script>
</body>
</html>

QML Change Size of Dropitem on Drop

I want to change the size of a Rectangle in the QML drag and drop example on drop to the size of the drop area. But somehow the Rectangle will not render to the new size although it takes the width and height values.
DragTile.qml
import QtQuick 2.0
Item
{
id: rootItem
property int boxWidth: 0
property int boxHeight: 0
property Item dragParent
width: boxWidth
height: boxHeight
MouseArea
{
id: mouseArea
width: rootItem.boxWidth
height: rootItem.boxHeight
drag.target: msgTile
onReleased:
{
parent = msgTile.Drag.target !== null ? msgTile.Drag.target : rootItem
rootItem.boxHeight = parent.boxHeight
rootItem.boxWidth = parent.boxWidth
}
Rectangle
{
id: msgTile
color: "cyan"
width: mouseArea.width
height: mouseArea.height
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
Drag.active: mouseArea.drag.active
Drag.hotSpot.x: width / 2
Drag.hotSpot.y: height / 2
//draging
states: State
{
when: mouseArea.drag.active
ParentChange { target: msgTile; parent: dragParent } // draw on top while draging
AnchorChanges { target: msgTile; anchors.verticalCenter: undefined; anchors.horizontalCenter: undefined }
}
}
}
}
The dragsource as well as the droparea take the size of a parent rectangle
in Main.qml
Rectangle
{
x: 100
y: 100
width: 100
height: 300
border.width: 1
DragSource
{
msgCount: 2
msgCapacity: 10
dragParent: topItem
}
}
Rectangle
{
x: 400
y: 100
width: 150
height: 300
border.width: 1
MsgDrop
{
msgCapacity: 12
}
}
The size of the droparea is a little bit larger, than the size of the dragtiles
Is there any order of rendering that I am missing?
I would be very happy for some advice.
Best regards

fabricjs.com stickman: move the lines and affect the related circles

using the stickman example of http://fabricjs.com/,
I have been trying to achieve moving the related circles when a line is moved. The code in the example is not well structured, heavy & with errors :), as I can not to move the the related circles symmetrically.
If in the //move the other circle part is used next line
obj.set({
'left': (s.calcLinePoints().x1 + _l),
'top': (-s.calcLinePoints().y1 + _t)
});
the difference is in the sign of collected information for y1 and we move some horizontal line visually the result OK, but in my opinion this type of "adjustment" is not the correct one...
[example code]
$(function() {
//create the fabriccanvas object & disable the canvas selection
var canvas = new fabric.Canvas('c', {
selection: false
});
//move the objects origin of transformation to the center
fabric.Object.prototype.originX = fabric.Object.prototype.originY = 'center';
function makeCircle(left, top, line1, line2, usedLine, usedEnd) {
//used line - used line for the center
//usedEnd - fromt the used line
var c = new fabric.Circle({
left: left,
top: top,
strokeWidth: 2,
radius: 6,
fill: '#fff',
stroke: '#666'
});
c.hasControls = c.hasBorders = false;
c.line1 = line1;
c.line2 = line2;
//add information which line end is used for center
var _usedLineName;
if (usedLine == 1) {
_usedLineName = line1.name;
} else {
_usedLineName = line2.name;
}
c.usedLineName = _usedLineName;
c.usedEndPoint = usedEnd;
return c;
}
function makeLine(coords, name) {
var l = new fabric.Line(coords, {
stroke: 'red',
strokeWidth: 4,
selectable: true, //false
name: name
});
l.hasControls = l.hasBorders = false;
return l;
}
//initial shape information
var line = makeLine([250, 125, 350, 125], "l1"),
line2 = makeLine([350, 125, 350, 225], "l2"),
line3 = makeLine([350, 225, 250, 225], "l3"),
line4 = makeLine([250, 225, 250, 125], "l4");
canvas.add(line, line2, line3, line4);
canvas.add(
makeCircle(line.get('x1'), line.get('y1'), line4, line, 1, 2),
makeCircle(line.get('x2'), line.get('y2'), line, line2, 1, 2),
makeCircle(line2.get('x2'), line2.get('y2'), line2, line3, 1, 2),
makeCircle(line3.get('x2'), line3.get('y2'), line3, line4, 1, 2));
canvas.on('object:moving', function(e) {
//find the moving object type
var objType = e.target.get('type');
var p = e.target;
if (objType == 'circle') {
p.line1 && p.line1.set({
'x2': p.left,
'y2': p.top
});
p.line2 && p.line2.set({
'x1': p.left,
'y1': p.top
});
//set coordinates for the lines - should be done if element is moved programmely
p.line2.setCoords();
p.line1.setCoords();
canvas.renderAll();
} else if (objType == 'line') {
//loop all circles and if some is with coordinates as some of the ends - to change them
for (var i = 0; i < canvas.getObjects('circle').length; i++) {
var currentObj = canvas.getObjects('circle')[i];
if (currentObj.get("usedLineName") == e.target.get('name')) {
//usedEndPoint=2
for (var ss = 0; ss < canvas.getObjects('line').length; ss++) {
var s = canvas.getObjects('line')[ss];
//console.log(s.calcLinePoints())
//console.log(s.calcLinePoints().y2)
var _l = s.left;
var _t = s.top;
if (s.get("name") == currentObj.get("usedLineName")) {
currentObj.set({
'left': (s.calcLinePoints().x2 + _l),
'top': (s.calcLinePoints().y2 + _t)
});
console.log(s.calcLinePoints().y2 + _t)
currentObj.setCoords();
currentObj.line1 && currentObj.line1.set({
'x2': currentObj.left,
'y2': currentObj.top
});
currentObj.line2 && currentObj.line2.set({
'x1': currentObj.left,
'y1': currentObj.top
});
currentObj.line2.setCoords();
currentObj.line1.setCoords();
//move the other circle
canvas.forEachObject(function(obj) {
var _objType = obj.get('type');
if (_objType == "circle" && obj.line2.name == s.get("name")) {
obj.set({
'left': (s.calcLinePoints().x1 + _l),
'top': (s.calcLinePoints().y1 + _t)
});
console.log(s.calcLinePoints().y1 + _t)
obj.setCoords();
obj.line1 && obj.line1.set({
'x2': obj.left,
'y2': obj.top
});
obj.line2 && obj.line2.set({
'x1': obj.left,
'y1': obj.top
});
obj.line2.setCoords();
obj.line1.setCoords();
//canvas.renderAll();
}
});
canvas.renderAll();
//end move oter
}
}
}
}
}
});
});
<script src="http://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.5.0/fabric.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<canvas id="c" width="500" height="500"></canvas>
Here it is the code on jsfiddle, too:
https://jsfiddle.net/muybien/mzsa3z9L/
I want previously to thank you, even only for reading the question.
Thanks of MiltoxBeyond's suggestion, the problem is fixed.
Here it is a working and little cleaned example:
//to save the old cursor position: used on line mooving
var _curX, _curY;
$(function() {
//create the fabriccanvas object & disable the canvas selection
var canvas = new fabric.Canvas('c', {
selection: false
});
//move the objects origin of transformation to the center
fabric.Object.prototype.originX = fabric.Object.prototype.originY = 'center';
function makeCircle(left, top, line1, line2) {
var c = new fabric.Circle({
left: left,
top: top,
strokeWidth: 2,
radius: 6,
fill: '#fff',
stroke: '#666'
});
c.hasControls = c.hasBorders = false;
c.line1 = line1;
c.line2 = line2;
return c;
}
function makeLine(coords, name) {
var l = new fabric.Line(coords, {
stroke: 'red',
strokeWidth: 4,
selectable: true, //false
name: name
});
l.hasControls = l.hasBorders = false;
return l;
}
//initial shape information
var line = makeLine([250, 125, 350, 125], "l1"),
line2 = makeLine([350, 125, 350, 225], "l2"),
line3 = makeLine([350, 225, 250, 225], "l3"),
line4 = makeLine([250, 225, 250, 125], "l4");
canvas.add(line, line2, line3, line4);
canvas.add(
makeCircle(line.get('x1'), line.get('y1'), line4, line), makeCircle(line.get('x2'), line.get('y2'), line, line2), makeCircle(line2.get('x2'), line2.get('y2'), line2, line3), makeCircle(line3.get('x2'), line3.get('y2'), line3, line4)
);
canvas.on('object:selected', function(e) {
//find the selected object type
var objType = e.target.get('type');
if (objType == 'line') {
_curX = e.e.clientX;
_curY = e.e.clientY;
//console.log(_curX);
//console.log(_curY);
}
});
canvas.on('object:moving', function(e) {
//find the moving object type
var p = e.target;
var objType = p.get('type');
if (objType == 'circle') {
p.line1 && p.line1.set({
'x2': p.left,
'y2': p.top
});
p.line2 && p.line2.set({
'x1': p.left,
'y1': p.top
});
//set coordinates for the lines - should be done if element is moved programmely
p.line2.setCoords();
p.line1.setCoords();
canvas.renderAll();
} else if (objType == 'line') {
var _curXm = (_curX - e.e.clientX);
var _curYm = (_curY - e.e.clientY);
//console.log("moved: " + _curXm);
//console.log("moved: " + _curYm);
//loop all circles and if some contains the line - move it
for (var i = 0; i < canvas.getObjects('circle').length; i++) {
var currentObj = canvas.getObjects('circle')[i];
if (currentObj.line1.get("name") == p.get('name') || currentObj.line2.get("name") == p.get('name')) {
currentObj.set({
'left': (currentObj.left - _curXm),
'top': (currentObj.top - _curYm)
});
currentObj.setCoords();
currentObj.line1 && currentObj.line1.set({
'x2': currentObj.left,
'y2': currentObj.top
});
currentObj.line2 && currentObj.line2.set({
'x1': currentObj.left,
'y1': currentObj.top
});
currentObj.line2.setCoords();
currentObj.line1.setCoords();
}
}
_curX = e.e.clientX;
_curY = e.e.clientY;
}
});
});
canvas {
border: 1px solid #808080;
}
<script src="http://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.5.0/fabric.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<canvas id="c" width="500" height="500"></canvas>

leaflet : Prevent marker to be dragged outside the map container

Please consider the following code http://jsfiddle.net/franckl/311bcbc8/
var southWest = L.latLng(-90, -180),
northEast = L.latLng(90, 180);
var bounds = L.latLngBounds(southWest, northEast);
var map = L.map('map', {
minZoom: 2,
zoomControl: false,
attributionControl: false,
maxBounds: bounds
});
// Using cartoDB basemap
L.tileLayer('https://cartodb-basemaps-{s}.global.ssl.fastly.net/light_all/{z}/{x}/{y}.png', {
minZoom: 2,
subdomains: 'abcd',
detectRetina: true,
attribution: ''
}).addTo(map);
map.fitBounds(bounds);
var newMarker0 = L.marker(map.getCenter(), {
icon: new L.Icon.Default(),
zIndexOffset: 10000,
draggable: true
});
newMarker0.addTo(map);
html
<div id="mycontainer">
<div id="map"></div>
</div>
css
body {
margin:0;
padding:0;
}
#map {
position:absolute;
top:0;
bottom:0;
width:300px;
}
#mycontainer {
top: 10px;
width: 600px;
height: 250px;
position: relative;
}
If you drag the marker to the right, it leaves visible area of the map.
How can I prevent the user from dragging the marker outside the map ?
Thanks !
answering my own question in case it helps anyone.
We detect the map container size and check if the marker is going outside the visible area by converting its lat/lng coordinates to a container point (map.containerPointToLatLng(markerContainerPosition))
As a bonus, this code leaves the marker in the same position relative to the map container when the user moves the map. It ensures that the marker never goes outside the visible area (even when zooming)
var southWest = L.latLng(-90, -180),
northEast = L.latLng(90, 180);
var bounds = L.latLngBounds(southWest, northEast);
var map = L.map('map', {
minZoom: 2,
zoomControl: false,
attributionControl: false,
maxBounds: bounds
});
// Using cartoDB basemap
L.tileLayer('https://cartodb-basemaps-{s}.global.ssl.fastly.net/light_all/{z}/{x}/{y}.png', {
minZoom: 2,
subdomains: 'abcd',
detectRetina: true,
attribution: ''
}).addTo(map);
map.fitBounds(bounds);
var newMarker0 = L.marker(map.getCenter(), {
icon: new L.Icon.Default(),
zIndexOffset: 10000,
draggable: true
});
newMarker0.addTo(map);
var mapSize = map.getSize();
var markerContainerPosition = map.latLngToContainerPoint(newMarker0.getLatLng());
function mapMove() {
newMarker0.setLatLng(map.containerPointToLatLng(markerContainerPosition));
}
function markerDrag(e) {
var mTempContainerPos = map.latLngToContainerPoint(newMarker0.getLatLng());
var newPos;
if (mTempContainerPos.x < 20) {
if (mTempContainerPos.y < 45) {
newPos = L.point(20, 45);
} else if (mTempContainerPos.y > (mapSize.y - 20)) {
newPos = L.point(20, mapSize.y - 20);
} else {
newPos = L.point(20, mTempContainerPos.y);
}
} else if (mTempContainerPos.x > mapSize.x - 20) {
if (mTempContainerPos.y < 45) {
newPos = L.point(mapSize.x - 20, 45);
} else if (mTempContainerPos.y > (mapSize.y - 20)) {
newPos = L.point(mapSize.x - 20, mapSize.y - 20);
} else {
newPos = L.point(mapSize.x - 20, mTempContainerPos.y);
}
} else {
if (mTempContainerPos.y < 45) {
newPos = L.point(mTempContainerPos.x, 45);
} else if (mTempContainerPos.y > (mapSize.y - 20)) {
newPos = L.point(mTempContainerPos.x, mapSize.y - 20);
}
}
if (newPos) {
markerContainerPosition = newPos;
newMarker0.setLatLng(map.containerPointToLatLng(newPos));
} else {
markerContainerPosition = mTempContainerPos;
}
}
map.on('move', mapMove);
newMarker0.on('drag', markerDrag);
A solution with slightly more generic code and tailored to dragging the marker rather than the map, but derivative of #Franckl's:
onMarkerDrag: function (event) {
// keep dragged marker within map bounds
var containerPoint = this.map.latLngToContainerPoint(event.target.getLatLng()),
clampX = null,
clampY = null,
MARKER_MARGIN = 10;
if (containerPoint.x - MARKER_MARGIN < 0) {
clampX = MARKER_MARGIN;
} else if (containerPoint.x + MARKER_MARGIN > this.mapContainerBounds.width) {
clampX = this.mapContainerBounds.width - MARKER_MARGIN;
}
if (containerPoint.y - MARKER_MARGIN < 0) {
clampY = MARKER_MARGIN;
} else if (containerPoint.y + MARKER_MARGIN > this.mapContainerBounds.height) {
clampY = this.mapContainerBounds.height - MARKER_MARGIN;
}
if (clampX !== null || clampY !== null) {
if (clampX !== null) { containerPoint.x = clampX; }
if (clampY !== null) { containerPoint.y = clampY; }
marker.setLatLng(this.map.containerPointToLatLng(containerPoint));
}
},
I derive this.mapContainerBounds once on map init instead of every time the drag handler fires (my map does not change size), like this:
this.mapContainerBounds = mapDOMNode.getBoundingClientRect();

How to add EventListener for dynamic contents in iphone

I am developing iphone application in Titanium. Unable to set addEventListener to my dynamically receiving contents.
Here is my code:
var receivedAccountsLength = Ti.App.userAccounts.length;
var topFrom = 100;
for(var i=1;i<receivedAccountsLength;i++)
{
var cont = Ti.UI.createLabel({text: Ti.App.userAccounts[i].accountName , width: 100, height: 30, borderWidth: 1, top: topFrom });
win.add(cont);
cont.addEventListener('click', function()
{
alert("cont");
});
topFrom += 50;
}
can any one..
#suresh Try this code this is Absolute working for you.
first you get your "eventListener object" then you can get you its property,
for help just Copy paste this code
var receivedAccountsLength = Ti.App.userAccounts.length;
var topFrom = 100;
for(var i=1;i<receivedAccountsLength;i++)
{
var cont = Ti.UI.createLabel({text: Ti.App.userAccounts[i].accountName , width: 100, height: 30, borderWidth: 1, top: topFrom });
cont.addEventListener('click', function(event)
{
alert("cont : "+ event.source.text);
});
win.add(cont);
topFrom += 50;
}
If, working the enjoy Titanium .....Cheers...!