Detecting Overlapping objects in Paper JS - paperjs

One rectangle and a couple of circles are on canvas. Scattered.
The circles which are on top of rectangle needs to be of different color.
Circle must be completely inside the rectangle.
How can I detect this in Paper JS.

The method item.isInside(rectangle) seems to perfectly match your need.
Here is a sketch demonstrating the solution.
// create 3 circles scattered on the canvas.
var circles = [
new Path.Circle({
center: view.center - 100,
radius: 50,
fillColor: 'orange'
}),
new Path.Circle({
center: view.center,
radius: 50,
fillColor: 'orange'
}),
new Path.Circle({
center: view.center + 150,
radius: 50,
fillColor: 'orange'
})
];
// Create a rectangle.
var rectangle = new Path.Rectangle({
from: view.center - 80,
to: view.center + 80,
strokeColor: 'black'
});
// Scale things up so that we can see better.
project.activeLayer.fitBounds(view.bounds.scale(0.8));
// For each circle...
circles.forEach(circle => {
// ...if circle is contained in rectangle bounds...
if (circle.isInside(rectangle.bounds)) {
// ...change circle color.
circle.fillColor = 'blue';
}
});

Related

QML: Draw geofence area on QML Map

Any ideas how to draw custom geofence area in QML (Qt Location module) similar to this:
Something similar to MapCircle or MapPolygon but filled outside of the region? And have custom filling pattern?
You probably can play with one of MapItems, for example MapQuickItem allows you put any QML item inside, for example I use Canvas for that:
Plugin {
id: mapPlugin
name: "osm"
}
Map {
id: map
anchors.fill: parent
plugin: mapPlugin
center: QtPositioning.coordinate(59.91, 10.75)
zoomLevel: 14
MapQuickItem {
id: marker
anchorPoint.x: image.width / 2
anchorPoint.y: image.height / 2
coordinate: map.center
sourceItem: Canvas {
id: image
width: map.width
height: map.height
onPaint: {
var ctx = getContext("2d");
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(image.width, 0);
ctx.lineTo(image.width, image.height);
ctx.lineTo(0, image.height);
ctx.lineTo(0, 0);
ctx.moveTo(image.width/2 + 50, image.height/2);
ctx.arc(image.width/2, image.height/2, 50, 0, 2 * Math.PI,0, true);
ctx.closePath();
ctx.fillStyle = Qt.rgba(1.0, 0, 0, 0.5);
ctx.strokeStyle = "red"
ctx.lineWidth = 10;
ctx.fill();
ctx.stroke();
}
}
}
}
Pay attention, the arc's counterclockwise is true to make a hole.
In the same way you can add images etc. But in a real project I would use some custom QQuickItem based item, just for better performance.

QML ShapePath clear path elements

I want to remove all path elements from my ShapePath. Since pathElements is a Qml list, the only way to modify it is by setting it to a new Javascript array. Therefore, I expected to be able to clear it by just assigning an empty array to it.
I tried path.pathElements = [], which does not work for me.
Then I tried path.pathElements = null, which does work (PathLine is no longer drawn), but it prints this ugly error message: QObject::connect: Cannot connect (nullptr)::changed() to QQuickShapePath::processPath()
Any other ideas?
Code to reproduce:
Shape {
anchors.fill: parent
ShapePath {
id: path
strokeWidth: 2
strokeColor: "green"
fillColor: "green"
Component.onCompleted: path.pathElements = []
PathLine { x: 50; y: 50 }
}
}
I filed a bug report at Qt and they confirmed my bug.
The workaround until this gets fixed is to first assign null, followed by [].
Shape {
anchors.fill: parent
ShapePath {
id: path
strokeWidth: 2
strokeColor: "green"
fillColor: "green"
Component.onCompleted: {
path.pathElements = null
path.pathElements = []
}
PathLine { x: 50; y: 50 }
}
}

How does one make two bodies join together on the sides nearest the collision point in QML Box2D?

I have a QML Box2D project that I'm working on which has a number of different Polygons in the form of Box2D Dynamic Bodies with Polygon Fixtures that all move dynamically around a World.
My goal is to make it so that whenever two different bodies collide with one another, they will both join on whatever sides that they collided on initially by matching the two sides up face to face and essentially joining into a single Body but remaining as two separate bodies in order to track them using the game's logic.
It will be something like two magnets floating in space and connecting to each other, and then having the two magnetic sides attracting one another and becoming fused together in a sense.
While I have had no issues creating the Bodies, and similarly no issues with determining when collisions happen and performing various functions when they do collide, I am not able to get the two objects to simply combine on a single side nearest the collision..
Here's what I've tried to do so far, without success:
Body {
id: body
world: physicsWorld
property var vertices
bodyType: Body.Dynamic
target: gamePiece
function beginContact(other) {
if (other.getBody() !== wallBody) {
var newObject = Qt.createQmlObject('import QtQuick 2.9; import Box2D 2.0; DistanceJoint { }', body, "dynamicSnippet1");
newObject.bodyA = body;
newObject.bodyB = other.getBody();
newObject.length = 80;
DistanceJoint.collideConnected = true;
body.angularVelocity = 0;
body.fixedRotation = true;
console.log("Created Distance Joint " + newObject);
} else {
console.log("Skipping Body collision with wall");
}
}
fixtures: Polygon {
density: 2
friction: 0.9
restitution: 0.01
vertices: body.vertices
onBeginContact: { body.beginContact(other) }
}
}
Every object that collides with another Body like it will simply become pulled into the colliding Body completely, and the sides do not match up at all.
How would I determine what the sides are of the Bodies that make contact and how is the best way to connect them?
I guess WeldJoint fits much better, for example:
import QtQuick 2.11
import QtQuick.Window 2.11
import Box2D 2.0
Window {
visible: true
width: 800
height: 600
title: qsTr("Hello World")
id: root
World {
id: physicsWorld
gravity: Qt.point(0, 0)
}
Repeater {
model: [
{ "x": 0, "y": 0, "width": 10, "height": root.height },
{ "x": root.width - 10, "y": 0, "width": 10, "height": root.height },
{ "x": 10, "y": 0, "width": root.width - 20, "height": 10 },
{ "x": 10, "y": root.height - 10, "width": root.width - 20, "height": 10 }
]
delegate: Rectangle {
id: wall
x: modelData.x
y: modelData.y
width: modelData.width
height: modelData.height
color: "lightgreen"
Body {
bodyType: Body.Static
target: wall
fixtures: Box {
width: wall.width
height: wall.height
friction: 0.5
density: 0.5
}
}
}
}
Rectangle {
id: item1
height: 100
width: 100
color: "orange"
antialiasing: true
smooth: true
x: 100
y: 100
Body {
id: itemBody1
bodyType: Body.Dynamic
target: item1
fixtures: Box {
density: 0.1
friction: 0.1
restitution: 1
width: item1.width
height: item1.height
onBeginContact: {
var body = other.getBody();
if(body === itemBody2)
{
var newJoint = linkJoint.createObject(root);
newJoint.bodyA = itemBody1;
newJoint.bodyB = body;
}
}
}
}
Component.onCompleted: {
var x = ((Math.random() * 800) - 400) / 200;
var y = ((Math.random() * 600) - 300) / 200;
itemBody1.applyLinearImpulse(Qt.point(x, y), Qt.point(50,50))
}
}
Rectangle {
id: item2
height: 100
width: 100
color: "lightblue"
antialiasing: true
smooth: true
x: 600
y: 100
Body {
id: itemBody2
bodyType: Body.Dynamic
target: item2
fixtures: Box {
density: 0.1
friction: 0.1
restitution: 1
width: item1.width
height: item1.height
}
}
Component.onCompleted: {
var x = ((Math.random() * 800) - 400) / 200;
var y = ((Math.random() * 600) - 300) / 200;
itemBody2.applyLinearImpulse(Qt.point(x, y), Qt.point(50,50))
}
}
Component {
id: linkJoint
WeldJoint {
localAnchorA: Qt.point(50, 50)
localAnchorB: Qt.point(150, 150)
collideConnected: true
}
}
}
Sure, you have to play with distances and angles here to fit your needs.

Proper shadow drop on rotation

I am trying to create a rotatable triangle that will always cast a shadow on its base. So far I was able to create the code for making and rotating the triangle but the shadow part is problematic. Here is my minimal code example:
import QtQuick 2.9
import QtQuick.Window 2.2
import QtGraphicalEffects 1.0
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
Rectangle
{
id: rectMain
anchors.centerIn: parent
width: parent.width
height: parent.height
color: "white"
Canvas
{
anchors.fill: parent
// set properties with default values
property real hFactor: 1 // height factor
property real trbase: 200
property color strokeColor: "black"
property color fillColor: "yellow"
property int lineWidth: 1
property real alpha: 1
property real rotAngle: 0
property real parentWidth: parent.width; // try
property real parentHeight: parent.height;
onStrokeColorChanged: requestPaint();
onFillColorChanged: requestPaint();
onLineWidthChanged: requestPaint();
onPaint:
{
hFactor = Math.abs(hFactor)
var ctx = getContext("2d") // get context to draw with
ctx.clearRect(0, 0, width, height); // remove what is painted so far
ctx.lineWidth = lineWidth
ctx.strokeStyle = strokeColor
ctx.fillStyle = fillColor
ctx.globalAlpha = alpha
ctx.save();
ctx.beginPath();
ctx.translate(parentWidth / 2, parentHeight / 2);
ctx.rotate((Math.PI / 180) * rotAngle);
ctx.moveTo(0, 0);
// drawing part, first calculate height using Pythagoras equation
var trheight = Math.sqrt(Math.pow(trbase, 2) - Math.pow(trbase / 2, 2));
trheight = trheight * hFactor;
var hfBase = trbase * hFactor;
ctx.lineTo(hfBase / -2, trheight); // left arm
ctx.lineTo(hfBase / 2, trheight); // right arm
ctx.closePath(); // base drawn automatically
ctx.fill();
ctx.stroke();
ctx.restore();
}
DropShadow
{
anchors.fill: parent
horizontalOffset: 0
verticalOffset: 3
radius: 3
samples: 7
color: "#80000000"
source: parent
}
}
}
}
I am facing the following problems:
The parser gives me some errors about ShaderEffectSource, that recursive property has to be set, but I dont know whos property is that.
Starting M:\bitbucket\qtworkspace\build-mwe-Desktop_Qt_5_11_0_MinGW_32bit-Debug\debug\mwe.exe...
QML debugging is enabled. Only use this in a safe environment.
ShaderEffectSource: 'recursive' must be set to true when rendering recursively.
ShaderEffectSource: 'recursive' must be set to true when rendering recursively.
ShaderEffectSource: 'recursive' must be set to true when rendering recursively.
ShaderEffectSource: 'recursive' must be set to true when rendering recursively.
QObject::disconnect: No such signal QObject::screenChanged(QScreen*) in items\qquickscreen.cpp:476
M:/bitbucket/qtworkspace/build-mwe-Desktop_Qt_5_11_0_MinGW_32bit-Debug/debug/mwe.exe exited with code 0
Somewhere in the web I have found a bug report that DropShadow property source cannot accept parent but since Canvas cannot be assigned with id, I am not sure how to do it. Other than this, the shadow renders correctly for rotAngle at 0 degrees:
It doesn't rotate correctly after adding some angle, for example 45 degrees:
It seems to me that the shadow is not being rotated together with the polygon. Can this be adjusted? The last thing is: How to hide shadow behind the polygon? At the moment its interfering with it.
I am not sure either the parser error is connected or not, that's why I have added it together here.
since Canvas cannot be assigned with id
Why Canvas can't be assigned with id?
The following code won't have that warning.
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
Rectangle
{
id: rectMain
anchors.centerIn: parent
width: parent.width
height: parent.height
color: "white"
Canvas
{
id: canvas
anchors.fill: parent
// set properties with default values
property real hFactor: 1 // height factor
property real trbase: 200
property color strokeColor: "black"
property color fillColor: "yellow"
property int lineWidth: 1
property real alpha: 1
property real rotAngle: 0
property real parentWidth: parent.width; // try
property real parentHeight: parent.height;
onStrokeColorChanged: requestPaint();
onFillColorChanged: requestPaint();
onLineWidthChanged: requestPaint();
onPaint:
{
hFactor = Math.abs(hFactor)
var ctx = getContext("2d") // get context to draw with
ctx.clearRect(0, 0, width, height); // remove what is painted so far
ctx.lineWidth = lineWidth
ctx.strokeStyle = strokeColor
ctx.fillStyle = fillColor
ctx.globalAlpha = alpha
ctx.save();
ctx.beginPath();
ctx.translate(parentWidth / 2, parentHeight / 2);
ctx.rotate((Math.PI / 180) * rotAngle);
ctx.moveTo(0, 0);
// drawing part, first calculate height using Pythagoras equation
var trheight = Math.sqrt(Math.pow(trbase, 2) - Math.pow(trbase / 2, 2));
trheight = trheight * hFactor;
var hfBase = trbase * hFactor;
ctx.lineTo(hfBase / -2, trheight); // left arm
ctx.lineTo(hfBase / 2, trheight); // right arm
ctx.closePath(); // base drawn automatically
ctx.fill();
ctx.stroke();
ctx.restore();
}
}
DropShadow
{
anchors.fill: canvas
horizontalOffset: 0
verticalOffset: 3
radius: 3
samples: 7
color: "#80000000"
source: canvas
}
}
}
About the warning:
I believe that should not consider as a BUG. DropShadow is an Item, so if you nest with its parent, it will do like render a shadow on the shadow.
Why didn't the shadow rotated?
Because you are rotating the contents of Canvas.
Even rotate the polygon, these properties won't change. Always drop the shadow vertically.
horizontalOffset: 0
verticalOffset: 3
Rotate both:
Item {
anchors.fill: parent
rotation: 30
Canvas
{
id: canvas
anchors.fill: parent
// set properties with default values
property real hFactor: 1 // height factor
property real trbase: 200
property color strokeColor: "black"
property color fillColor: "yellow"
property int lineWidth: 1
property real alpha: 1
property real parentWidth: parent.width; // try
property real parentHeight: parent.height;
onStrokeColorChanged: requestPaint();
onFillColorChanged: requestPaint();
onLineWidthChanged: requestPaint();
onPaint:
{
hFactor = Math.abs(hFactor)
var ctx = getContext("2d") // get context to draw with
ctx.clearRect(0, 0, width, height); // remove what is painted so far
ctx.lineWidth = lineWidth
ctx.strokeStyle = strokeColor
ctx.fillStyle = fillColor
ctx.globalAlpha = alpha
ctx.save();
ctx.beginPath();
ctx.translate(parentWidth / 2, parentHeight / 2);
ctx.moveTo(0, 0);
// drawing part, first calculate height using Pythagoras equation
var trheight = Math.sqrt(Math.pow(trbase, 2) - Math.pow(trbase / 2, 2));
trheight = trheight * hFactor;
var hfBase = trbase * hFactor;
ctx.lineTo(hfBase / -2, trheight); // left arm
ctx.lineTo(hfBase / 2, trheight); // right arm
ctx.closePath(); // base drawn automatically
ctx.fill();
ctx.stroke();
ctx.restore();
}
}
DropShadow
{
anchors.fill: canvas
horizontalOffset: 0
verticalOffset: 3
radius: 3
samples: 7
color: "#80000000"
source: canvas
}
}

Snap text to a point

In my test application I am drawing triangles. In the down left corner of this triangle (lest call it nr 2) I need to add text that is just under the line. If the triangle rotates, the text should rotate as well. Heres an "explanation" image:
And here is the code:
import QtQuick 2.9
import QtQuick.Window 2.2
import QtGraphicalEffects 1.0
Window
{
visible: true
width: 640
height: 480
title: qsTr("Hello World")
Rectangle
{
id: rectMain
anchors.centerIn: parent
width: parent.width
height: parent.height
color: "white"
Item
{
anchors.fill: parent
rotation: 0
property real trheight: 0;
property real hfBase: 0;
Canvas
{
id: canvas
anchors.fill: parent
// set properties with default values
property real hFactor: 1 // height factor
property real trbase: 200
property color strokeColor: "black"
property color fillColor: "yellow"
property int lineWidth: 1
property real alpha: 1
property real parentWidth: parent.width; // try
property real parentHeight: parent.height;
onStrokeColorChanged: requestPaint();
onFillColorChanged: requestPaint();
onLineWidthChanged: requestPaint();
onPaint:
{
hFactor = Math.abs(hFactor)
var ctx = getContext("2d") // get context to draw with
ctx.clearRect(0, 0, width, height); // remove what is painted so far
ctx.lineWidth = lineWidth
ctx.strokeStyle = strokeColor
ctx.fillStyle = fillColor
ctx.globalAlpha = alpha
ctx.save();
ctx.beginPath();
ctx.translate(parentWidth / 2, parentHeight / 2);
ctx.moveTo(0, 0);
// drawing part, first calculate height using Pythagoras equation
parent.trheight = (Math.sqrt(Math.pow(trbase, 2) -
Math.pow(trbase / 2, 2))) * hFactor;
parent.hfBase = trbase * hFactor;
ctx.lineTo(parent.hfBase / -2, parent.trheight); // left arm
ctx.lineTo(parent.hfBase / 2, parent.trheight); // right arm
ctx.closePath(); // base drawn automatically
ctx.fill();
ctx.stroke();
ctx.restore();
}
}
DropShadow
{
anchors.fill: canvas
horizontalOffset: 0
verticalOffset: 3
radius: 3
samples: 7
color: "#80000000"
source: canvas
}
}
Text
{
//x: ?
//y: ?
font.family: "RobotoCondensed-Regular";
font.pointSize: 12;
text: qsTr("TEST");
opacity: 0.6;
}
}
}
So ideally the end effect would look like this (edited in paint):
Then on 30 degree rotation like this:
The easiest way would be to have the Text as a child item of the item that is rotated. Then you could just position it at the bottom of it and it would rotate with its parent.
However, your canvas covers the whole window, so your only option (given the current code) is to draw the text when you draw the shape. You can draw text with text(), fillText() or strokeText().
I would suggest rewriting your code to use the first approach I mentioned. Give each shape its own canvas, and make the text a child of the Canvas item (or its parent if you don't want the drop shadow effect to be applied to the text). Rotating an item is much cheaper than rotating by repainting a Canvas.

Resources