Implementing an eraser with paperjs - javascript

I've checked, similar questions have been asked, but have been left partially answered or not answered at all.
I need to build an eraser tool with PaperJS that will be able to erase existing paths, without erasing the image underneath.
So the image can reside on the first layer, and the paths on the second. See the accompanying JSFiddle: jsfiddle which shows two attempts:
paper.install(window);
var canvas = document.getElementById("myCanvas");
paper.setup(canvas);
//the yellow background (can also be background image) which should NOT be erased
var rect = new Rectangle(new Point(0, 0), new Point(paper.view.size.width, paper.view.size.height));
var rectPath = new Path.Rectangle(rect);
rectPath.fillColor = 'yellow';
//first attempt to erase the black line
var first = paper.project.activeLayer;
var second = new Layer();
var from = new Point(50, 0);
var to = new Point(50, 100);
var path1 = new Path.Line(from, to);
path1.strokeColor = 'black';
path1.strokeWidth = 40;
var from = new Point(0, 50);
var to = new Point(100, 50);
var path2 = new Path.Line(from, to);
path2.strokeColor = 'green'; //I've tried other colors as well, this seems irrelevant
path2.strokeWidth = 40;
path2.blendMode = 'destination-out';
//So I saw this
var path = new CompoundPath({
children: [
new Path.Circle({
center: new Point(200, 50),
radius: 30
}),
new Path.Circle({
center: new Point(200, 50),
radius: 10
})
],
fillColor: 'black',
selected: true
});
//second attempt
//I need to actually erase with one line through lots of paths, not sure how to implement it with this
var path = new CompoundPath({
children: [
new Path.Line({
from: [350, 0],
to: [350, 100],
strokeWidth: 30,
strokeColor: 'green' //irrelevant it seems
}),
new Path.Line({
from: [300, 50],
to: [400, 50],
strokeWidth: 30,
strokeColor: 'white' //irrelevant it seems
})
],
strokeColor: 'black',
selected: true,
strokeWidth: 20
});
paper.view.draw();
<script src="http://tmantechblog.com/testpaperjs/js/libs/paper.js"></script>
<canvas id="myCanvas" height='500' width='500'>This does not support HTML5 canvas</canvas>
The first attempt tries to use blendMode='destination-out' but it leaves a white mark rather than a transparent path
The second implementation tries to mimic the compoundPath example provided there, but where the paths cross, no hole is produced. Also, I'm unsure how this can be used to implement an eraser across multiple paths.
Any advice on how to proceed will be appreciated. Otherwise I'll have to port my whole project either to the basic HTML 5 canvas or FabricJS, or something similar.

Related

Openlayers trackline length (include altitude)

I using OpenLayers 4, and I want to get length at the LineString.
My code snippet:
var trackline = new ol.geom.LineString(points); //points an array with coordinates
var featurething = new ol.Feature({geometry: trackline});
var linestyle = new ol.style.Style({
stroke : new ol.style.Stroke({width: 4, color: 'rgba(0, 180, 220, 1)'})
});
featurething.setStyle(linestyle);
vectorSource.addFeature( featurething );
How get the trackline length, include altitude?
Thanks!

Paper.js - Clipping opacity for paths outside of area

I have a simple rectangle that forms the clipping area for all shapes added to the canvas, which is working great:
var area = new paper.Rectangle(
100, 100, 300, 120
);
var path = new paper.Path.Rectangle(area);
group.addChild(path);
group.clipped = true;
What I'm trying to achieve is instead of hiding the paths that fall outside of this area, they are shown with a slight opacity, something like:
Thanks in advance for any help and suggestions.
This is not a simple way as clipped, you might do it by using method intersect.
Please try this code.
// SET INITIAL
var area = new paper.Path.Rectangle(100, 100, 300, 220);
area.fillColor = 'yellow'
area.opacity = 0.2
var circle1 = new paper.Path.Circle({
center:[150, 150],
radius: 100,
fillColor: 'red'
})
// OPACITY CLIPPING
var circle2 = circle1.intersect(area)
circle1.opacity = 0.2

Three.js 3D Text Bending

Is this still available in r69? In the process of 'rolling' my own, but before moving forward, want to make sure I have not overlooked any vital documentation or useful libraries...
Try to make through this example. Look at messages in the console.
<script src="js/modifiers/BendModifier.js"></script>
var text = "THREE.BendModifier";
var textGeometry = new THREE.TextGeometry(text, {
size: 128,
height: 50,
curveSegments: 4,
font: "gentilis",
weight: "bold",
style: "normal",
bevelEnabled: true,
bevelThickness: 2,
bevelSize: 1,
});
var textMaterial = new THREE.MeshPhongMaterial({
color: 0x62254a
});
var text3D = new THREE.Mesh(textGeometry, textMaterial);
var direction = new THREE.Vector3(0, 0, -1);
var axis = new THREE.Vector3(0, 1, 0);
var angle = Math.PI / 6;
var modifier = new THREE.BendModifier();
modifier.set(direction, axis, angle).modify( text3D.geometry );
textGeometry.computeBoundingBox();
var textWidth = textGeometry.boundingBox.max.x - textGeometry.boundingBox.min.x;
text3D.position.set(-0.5 * textWidth, 500, 0);
scene.add(text3D);
Just wanted to mention that you need to change BendModifier.js according to here https://stackoverflow.com/a/40492948/7132939
to make it work with newer versions of three.js
BendModifier.js uses THREE.Matrix3.getInverse() which is not working any more.
So you need to change the BendModifier.js to use THREE.Matrix4.getInverse(..) and one other change as mentioned in the answer linked.
And generating text has changed - using a THREE.fontLoader()
Complete working example can be found following this link
Screenshot of web site - link
And full answer of the internal link:
There was a change in THREE.js but it is quite easy to adjust the BendModifier.js script. Just change the lines
var InverseP = new THREE.Matrix3().getInverse( P );
and
newVertices[i] = new THREE.Vector3(); newVertices[i].copy( geometry.vertices[i] ).applyMatrix3( InverseP );
to Matrix4 in both cases - so they will read:
var InverseP = new THREE.Matrix4().getInverse( P );
and
newVertices[i] = new THREE.Vector3(); newVertices[i].copy( geometry.vertices[i] ).applyMatrix4( InverseP );
Just tried it with three.min.81.js and it works fine.

Bouncing image balls with KineticJS

I'm just starting out the KineticJS library and been playing about with it creating shapes etc.. However, I'm struggling to create a custom circle with my own image in it. I have tried using the fillPattern but it doesn't scale/centre correctly at all. Am I meant to use my own circle image or a rectangle image and then let KineticJS take care of things?
Just to give a bit of background: What I want is 3 balls bouncing in and then settling in place.
Any advice is welcome.
Sorted it... needed the offset values
var ball = new Image();
ball.src = 'ball2.jpg';
ball.height = 230;
ball.width = 230;
var stage = new Kinetic.Stage({
container: 'container',
width: 1000,
height: 1000
});
var circle = new Kinetic.Circle({
x: 300,
y: 300,
radius: 115,
fillPatternImage: ball,
fillPatternOffset :{
x: -115,
y: -115
}
});
var layer = new Kinetic.Layer();
// add the shape to the layer
layer.add(circle);
// add the layer to the stage
stage.add(layer);
ball.onload = function () {
stage.draw();
}

Is it possible to use several images as the Google Maps v3 MarkerImage?

I want a map marker composed of three images. Two images will be the same across all markers where one is variable. There doesn't seem to be a way to specify multiple images on the MarkerImage class. The only way that I can think of is to draw all the images to a canvas then outputting it as a png dataURL for the MarkerImage class. That seems like overkill though.
I don't think it is possible with three separate images.
If two of the images are always the same I guess you could combine them into one and store it in the icon property and then use the variable image in the shadow property or vise versa (the icon will always be on top of the shadow). You just need to manipulate the size and position of the MarkerImage objects.
working example: http://jsfiddle.net/mG4uz/1/
var image1 = new google.maps.MarkerImage(
'http://google-maps-icons.googlecode.com/files/sailboat-tourism.png',
new google.maps.Size(45, 90), //adjust size of image placeholder
new google.maps.Point(0, 0), //origin
new google.maps.Point(0, 25) //anchor point
);
var image2 = new google.maps.MarkerImage(
'http://google-maps-icons.googlecode.com/files/sailboat-tourism.png',
new google.maps.Size(45, 45),
new google.maps.Point(0, 0),
new google.maps.Point(0, 0)
);
var marker = new google.maps.Marker({
position: centerPosition,
map: map,
icon: image1,
shadow:image2
});
I would try to define a new class which would inherit from google.maps.MarkerImage that would allow it and then just call marker.setIcon() with this object.
Also have a look here and here.
Yes, you can use multiple images and set them as one image for each Marker Icon in your array of the map markers.
What's more important is that you can use images that are on a remote server.
For example, in my case, I loaded an array of sites that each has an image for the site, so I'm merging an image of a marker with the image of the site (received from server).
Here is my solution:
mergeImages(element) {
var canvas: HTMLCanvasElement = this.canvas.nativeElement;
var context = canvas.getContext('2d');
let img1 = new Image();
let img2 = new Image();
img1.onload = function () {
canvas.width = img1.width;
canvas.height = img1.height;
img2.crossOrigin = 'anonymous';
img2.src = element.info.image || './assets/img/markers/markerIcon42.png';
};
img2.onload = () => {
context.clearRect(0, 0, canvas.width, canvas.height);
context.globalAlpha = 1.0;
context.drawImage(img1, 0, 0);
context.globalAlpha = 1; //Remove if pngs have alpha
context.globalCompositeOperation = 'destination-over';
if (element.info.image === null) {
context.drawImage(img2, 11, 4, 42, 42);
} else {
context.drawImage(img2, 15, 7, 35, 35);
}
var dataURL = canvas.toDataURL('image/png', 1.0);
console.log(dataURL);
this.markers.push({
lat: element.info.location.latitude,
lng: element.info.location.longitude,
controllerInfo: element.info,
icon: {
url: dataURL,
labelOrigin: { x: 32, y: -10 },
scaledSize: {
width: 64,
height: 64
},
origin: { x: 0, y: 0 },
anchoriconAnchor: { x: 7, y: 7 },
},
controllerStaus: element.status,
label: {
text: element.info.siteName,
color: "#ED2C2C",
fontWeight: "500",
fontSize: "14px"
}
});
console.log(this.markers);
};
img1.src = './assets/img/markers/map-marker64.png';
}
One Important thing to add is "img2.crossOrigin = 'anonymous';" this will prevent toDataUrl cors-orign error.
This solution is good also for clustering
I hope it will help
You can use an excellent small library, RichMarker. Its documentation is here.
You can even inherit it and create a custom marker class, something like this:
Ns.Marker = function(properties) {
RichMarker.call(this, properties);
this.setContent('<div class="three-images-marker">' +
properties.NsImage1 ? '<div class="marker-image-1"><img src="'+properties.NsImage1+'"/></div>' : '' +
properties.NsImage2 ? '<div class="marker-image-2"><img src="'+properties.NsImage2+'"/></div>' : '' +
properties.NsImage3 ? '<div class="marker-image-3"><img src="'+properties.NsImage3+'"/></div>'+
'</div>');
};
Ns.Marker.prototype = Object.create(RichMarker.prototype);
And then use it like this:
var gorgeousMarker = new Ns.Marker({
position: yourMarkerLatlng,
map: yourMap,
NsImage1: 'example.com/image1.png',
NsImage2: 'example.com/image2.png',
NsImage3: 'example.com/image3.png',
});
'Ns' is whatever namespace you use, if you do.
From here on it's CSS work, you can position the images as you like.

Categories

Resources