I am trying to get the id of the shape my mouse is currently hovering over.
my shapes are in a container
// creating the layers
gridLayer = new PIXI.DisplayObjectContainer ();
gridLayer.setInteractive(true);
stage.addChild(gridLayer);
and i am creating each shape like this;
function drawHexagon(x,y, size, gap,scale, color, iterI, iterJ, type) {
var shape = new PIXI.Graphics();
// set a fill and line style
shape.beginFill(color);
shape.lineStyle(1, 0xa0a0a0, 1);
size = size-gap;
for (i = 0; i < 7; i++) {
angle = 2 * Math.PI / 6 * (i + 0.5);
var x_i = x + size * Math.cos(angle);
var y_i = y + size * Math.sin(angle);
if (i === 0) {
shape.moveTo(x_i, scale *y_i)
}
else {
shape.lineTo(x_i, scale * y_i)
}
};
shape.endFill();
// calculate and save the axial coordinates
var cX = iterJ - (iterI - (iterI&1)) / 2;
var cZ = iterI;
var cY = -1*(cX+cZ);
shape.hexId = cX + "x" + cY + "y" + cZ + "z";
shape.hexPosX = x;
shape.hexPosY = y;
shape.setInteractive(true);
shape.mouseover = function(mouseData){
console.log("MOUSE OVER " + shape.hexId);
}
shape.click = function(mouseData){
console.log("MOUSE CLICK " + shape.hexId);
}
gridLayer.addChild(shape);
}
However, clicking on any shape or hovering over it is not showing me anything in the console. what am i doing wrong?
i have tried both
shape.setInteractive(true)
and
shape.interactive = true
but neither seems to work for me.
EDIT: i have added a jsfiddle. it doesnt works (i dont know how to link things in jsfiddle) but you can see my entire code in there.
http://jsfiddle.net/9aqHz/1/
For a PIXI.Graphics object to be interactive you need to set a hitArea shape (it can be a Rectangle, Circle or a Polygon):
shape.hitArea = new PIXI.Polygon([
new PIXI.Point(/* first point */),
new PIXI.Point(/* second point */),
new PIXI.Point(/* third point */),
new PIXI.Point(/* fourth point */),
new PIXI.Point(/* fifth point */)
]);
Another approach would be to generate a texture from the shape and use a Sprite, but the hit area would be the entire rectangular bounds of the hexagon:
var texture = shape.generateTexture();
var sprite = new PIXI.Sprite(texture);
sprite.setInteractive(true);
sprite.anchor.set(0.5, 0.5);
Fiddle with this applied to your example
#imcg I updated your code so it workes with Pixi 3.0.8
- sprite.setInteractive(true);
+ shape.interactive = true;
+ shape.buttonMode = true;
- sprite.setInteractive(true)
+ sprite.interactive = true;
+ sprite.buttonMode = true;
http://jsfiddle.net/LP2j8/56/
I will add a bit of info for anyone who is in the same boat i was in;
When you define a shape as a geom, you have to explicitly state a hitarea.
So adding the following code makes it work;
shape.hitArea = new PIXI.Polygon(vertices);
shape.interactive = true;
shape.click = function(mouseData){
console.log("MOUSE CLICK " + shape.hexId);
}
But, when you define a shape as a sprite/texture, you dont need to do this.
in cases of sprites, just setting shape.interactive = true for the sprite is sufficient. You dont need to set the interactive property for the parent object or the stage.
Related
I have an imported SVG in my paper.js project.
What i'm trying to do is to rotate each of the svg's segment points around a set radius, animating it with the onFrame() method.
I know how to position each segments point's x and y position but i cant really figure out how to position it in a 'rotation'.
What i have now :
var words = project.importSVG(document.querySelector("svg"));
words.visible = true; // Turn off the effect of display:none;;
words.position = view.center;
var letterR = words.children.letter_r;
var letterR_outside = letterR.children.letter_r_outside;
letterR_outside.selected = true;
var rotate_point = view.center;
function onFrame(event) {
var _delta = Math.sin(event.time);
// console.log(Math.round(50 * Math.cos(theta[0])) * _delta);
// letterR_outside.segments[0].point.x = Math.round(_radius * Math.cos(theta[0]));
// letterR_outside.segments[0].point.y = Math.round(_radius * Math.sin(theta[0]));
for (var i = 0; i < letterR_outside.segments.length; i++) {
var segment = letterR_outside.segments[i];
// segment.point.x += _delta;
segment.point.rotate(3, rotate_point);
}
}
Example of what i'm trying to achieve by rotating each points around a radius. [i made the black circles visible to show the rotation circle of each points]
I'm not exactly sure how to approach this. Any help is greatly appreciated
Calling the point.rotate() does nothing because it returns a clone of the point and doesn't modify it directly.
See the documentation:
Rotates the point by the given angle around an optional center point. The object itself is not modified. ...
So you might want to reasign the value:
segment.point = segment.point.rotate(3, rotate_point);
My implementation is this:
Detect if mousedown and mousemove and if true then draw and saved the points in an array.
In my mousemove I will convert the points that will be drawn in
I converted the curPath to (Date,value) then to (X and Y-axis) so that they will be saved in the implementation in my canvas.
My problem is that how will I detect points[] ? so that I can highlight it and drag as well.
UPDATE.
This is bigger than I expected. I will continue to improve the quality of the answer as I go. See the bottom of the answer for status.
Picking.
The simplest way is to check how far the mouse is from each point in the line, and highlight the line that has the closest point. The problem is that when you have many lines and lots of points it slows down and become unusable.
Another ways is to store some extra info on each line to help you vet lines that are not going to be picked. In the example I create a bounding box for each line and check if the mouse is inside or near that box. If so then I search the line some more checking each line segment and keeping the line that is closest to the mouse.
Some of the function to look at.
Helpers.prototype.resetExtent();
Helpers.prototype.extent();
Helpers.prototype.copyOfExtent();
Used to find the bounding box. (extent)
function refreshLine(line);
Called after a line is drawn, it takes a set of points drawn and adds the bounding box (extent), plus other stuff for the demo.
function findLineAt(x,y){
This function takes the x,y position of the mouse (or what ever) and returns the closest line within 20 pixels. It first checks the bounding box, if that passes it calls
Helpers.prototype.getDistToPath = function (line,x,y) {
This gets the line as just a set of points and checks the distance to the center of each line. It also checks if the check needs more details and calls the function.
Helpers.prototype.distPointToLine = function (x, y, x1, y1, x2, y2) {
This function will return the shortest distance from a point to a line. x,y is the point x1,y1,x2,y2 is the line. It does not check the line segment but the line which is infinitely long. Helpers.lineSegPos property will hold the normalised position of the closest point on the line. If you need it.
So back to findLineAt(x,y), after all those calls it will return the line if found or undefined if not.
Highlight and dragging.
Highlighting is up to you. I just cycle the hue of the line closest very quickly. You may wish to put a bounding box around it. Easy to do as all you do is redraw the closest line each update.
How its works
The main loop.
update()
Handles the main loop, is called 60 times a second and has to parts, the Draw section is for drawing and pick for picking. See if(drawMode==="Pick"). The mouse is only read in the update, the mouse is set independently by the mouse listener. At the end of every loop I save the mouse button state mouse.lastButton so that I can check when the mouse moves down and up.
In the pick part if the mouse is not down I call the findLineAt function. If I find a line (line !== undefined) I highlight the line by changing its colour and drawing it.
Because every update I have the current mouseButton state and what it was last update, I will know when the mouse button first moves down because mouse.button is true and mouse.lastButton is false. If there is a line near the mouse, I record the mouse position in dragOffX and dragOffY and set a flag dragging to true. I also draw the canvas onto another canvas to keep as background. At this point I also check which mouse button is down. If right button I copy the line and set it as the line to be dragged, or if the middle button I search all the lines to find its index in the lineArray and delete it, then just redraw.
Next update (1/60th second later) and dragging flag is true mouse.button is true and lastLine (the line that was closest) is not undefined I know I am dragging a line. I clear the canvas, draw the saved copy of the canvas (it's faster to draw that then redraw all the lines again especially if you have 100's of lines with 1000's of points), and then redraw the line I am dragging.
To workout where to draw the dragged line I get the distance the mouse is from dragOffX and dragOffY and set the transpose part of setTransform(1, 0 , 0, 1, mouse.x - dragOffX, mouse.y - dragOffY) to that distance. That has the effect of moving the line by the drag amount. I keep doing this until the mouse button is up.
Drop
Next update and mouse.button is up.
If the mouse button is up and the dragging flag is true then I must drop the line. At this point a get the mouse distance from dragOffX dragOffY and add it to each point in the line. Effectively moving the line. I also update the bounding box. I then clear the screen and redraw all the line, that removes the old position of the line from the canvas and put it at it's new home.
Done.
The code grew a little long. If this answer gets some support then I will clean it up some more. If not well then it does not matter..
It covers the basics of your question, detecting and moving points via mouse action. Highlighting and moving lines made of sets of points. My point are arrays of objects each with an x and y. Each line is stored in the lineArray a line has style, extent, id properties, and an array called line with all the points.
There is one mouse handler that takes the required mouse events. Move, mouse down and up, and mouse out. Mouse out stops the mouse locking up by turning the mouse buttons off. I also stop the context menu while the mouse is over the canvas.
I use requestAnimationFrame to call update to keep it all running smoothly.
I hope this helps you. I will improve it if it is what you are after. If not you will have to give a litte more info. Please do ask if you have problems.
Updates.
Added bounding box and improved the Helpers.prototype.getDistToPath(line,x,y) which I forgot to fix last night. Now its quicker and does not miss lines parallel to x and y axis. Moved screen redraw to accommodate the bounding box and add more comments.
Please do not hesitate to ask if you have question to this problem.
function log(){}; // duck up the logs
customCursors ={
encoding:"url('data:image/png;base64,",
drag_small : {
center : " 25 25,",
image : "iVBORw0KGgoAAAANSUhEUgAAADMAAAAzCAYAAAA6oTAqAAACQElEQVRoQ+2azW7DIAyAYZdJW6vlVmmnvcLe/yH2CjtN6i1Tu0m9rIMsJIYChmCvCWkuqZSA/fkPQyoF83VWl5RSqJtQd8kpjnVyB4QdiA0GghhvcHuIBcYH8h9A5DAxEG4gUhgN8rzbiY/9Hs1zjpAjg0nxiEtIDUQCMwWEI+SKYfJBzorDFkvloSvAXKZTs92K9nAoXlTJYFwV9YofunyNAEWHQALjU9qETijpA2OK9CkaHLJ8NYumBrzBoMss/sK6wkyHDLRJyp6EKsxyZUc9Y5R62mzE5/GYvB+hhNFVMVV+EMZVKGeVpoYxwYHp4IUp3VhxwehwjwFdwIQUwawC84oTJgZkwaQogRfIvzcA/DCkb1m63Eu9sE4CFqQBxgty+hLi/mHocnMOVyzFf96EuHv1AkKopmlE27YW5wiuDHD6Vvo8Ds/daOlggh7pYMbBqdaEnon9zpmve9ejDwSS0f3IRBgYGqOwF2W0dysEKWCskO4dkz1vbADMF9PaQ6OF8qBECT1ndZ6pJ2eMa6upZlGg/mFunF91ncGAFtcBxIDmApPVm4WA5gCD6bCO/Qz0EFzMFrvTnLoip3TfKUbJlb+uA41c60S7cPUQS+Ip8syYm2eg9dzjoMFK/edy19KxTqI0j4o9Y5LdVXqxXwFy+zYXfHbfZ9IPKWb85QyrXlh1oqxuxTmDdduJ22sSPUgmgUBV/A8gx0OUoWX1jVhMT3leVW8WKgpcHmFtZ3whxw2iZZIWAF9IOod/rPJ+AQ3iOFgpekFcAAAAAElFTkSuQmCC')"
},
}
function setCursor (name){
if(name === undefined){
canvas.style.cursor = "default";
}
if(customCursors[name] !== undefined){
var cur = customCursors[name];
canvas.style.cursor = customCursors.encoding + cur.image + cur.center + " pointer";
}else{
canvas.style.cursor = name;
}
}
// get canvas button and creat context2D
var canvas = document.getElementById("canV");
var ctx = canvas.getContext("2d");
var but = document.getElementById("redrawAllID");
but.addEventListener("click",function(){
if(drawMode === "Pick"){
drawMode = "Draw";
but.value = "Draw Mode";
}else{
drawMode = "Pick";
but.value = "Pick Mode";
lastLine = undefined;
backGroundImage.ctx.clearRect(0,0,backGroundImage.width,backGroundImage.height);
backGroundImage.ctx.drawImage(canvas,0,0);
}
})
// Groover Bitmaps API dependency replacement
// Extracted from Groover.Bitmaps
var createImage= function(w,h){ // create a image of requier size
var image = document.createElement("canvas");
image.width = w;
image.height =h;
image.ctx = image.getContext("2d"); // tack the context onto the image
return image;
}
var backGroundImage = createImage(canvas.width,canvas.height);
if(!mouse){
// get all the mouse events
canvas.addEventListener('mousemove',mouseMoveEvent);
canvas.addEventListener('mousedown',mouseMoveEvent);
canvas.addEventListener('mouseup' ,mouseMoveEvent);
canvas.addEventListener('mouseout' ,mouseMoveEvent);
canvas.addEventListener("contextmenu", function(e){ e.preventDefault();}, false);
// helper for random colour
var mouse = { // mouse data
x:0,
y:0,
button:false,
lastButton:false, // need this to see when the mouse goes down
which:[false,false,false],
};
}
function mouseMoveEvent(event){// handle all canvas mouse events as they come in
// get new mouse positions
mouse.x = event.offsetX;
mouse.y = event.offsetY;
if(mouse.x === undefined){ // if firefox
mouse.x = event.clientX;
mouse.y = event.clientY;
}
if(event.type === "mouseout"){
mouse.button = false;
mouse.which[0] = false;
mouse.which[1] = false;
mouse.which[2] = false;
}
if(event.type === "mousedown"){ // now see if there is extra info
mouse.button = true;
mouse.which[event.which-1] = true;
}
if(event.type === "mouseup"){ // now see if there is extra info
mouse.button = false;
mouse.which[event.which-1] = false;
}
event.preventDefault();
}
// because forEach is too slow
if (Array.prototype.each === undefined) {
Object.defineProperty(Array.prototype, 'each', {
writable : false,
enumerable : false,
configurable : false,
value : function (func) {
var i,
returned;
var len = this.length;
for (i = 0; i < len; i++) {
returned = func(this[i], i);
if (returned !== undefined) {
this[i] = returned;
}
}
}
});
}
// helper functions
function Helpers(){
}
Helpers.prototype.randomColour = function(){
return "hsl("+Math.floor(Math.random()*360)+",100%,50%)";
}
Helpers.prototype.occilatingColour = function(){
var t = (new Date()).valueOf()
return "hsl("+(Math.floor(t/2)%360)+",100%,50%)";
}
// used for building up the extent of a cloud of points
Helpers.prototype.resetExtent = function(){
if(this.extentObj === undefined){ // check if the extentObj is there
this.extentObj = {}; // if not create it
}
this.extentObj.minX = Infinity;
this.extentObj.minY = Infinity;
this.extentObj.maxX = -Infinity;
this.extentObj.maxY = -Infinity;
}
Helpers.prototype.extent = function( p) { // add a point to the extent
this.extentObj.minX = Math.min(this.extentObj.minX, p.x);
this.extentObj.minY = Math.min(this.extentObj.minY, p.y);
this.extentObj.maxX = Math.max(this.extentObj.maxX, p.x);
this.extentObj.maxY = Math.max(this.extentObj.maxY, p.y);
}
Helpers.prototype.copyOfExtent = function () { // get a copy of the extent object
return {
minX : this.extentObj.minX,
minY : this.extentObj.minY,
maxX : this.extentObj.maxX,
maxY : this.extentObj.maxY,
centerX : (this.extentObj.maxX-this.extentObj.minX)/2,
centerY : (this.extentObj.maxY-this.extentObj.minY)/2,
width:this.extentObj.maxX-this.extentObj.minX,
height:this.extentObj.maxY-this.extentObj.minY,
};
}
Helpers.prototype.getID = function(){ // return a unique ID for this session
if(this.id === undefined){
this.id = 0;
}
this.id += 1;
return this.id;
}
// function to get distance of point to a line
Helpers.prototype.distPointToLine = function (x, y, x1, y1, x2, y2) {
var px = x2 - x1;
var py = y2 - y1;
var u = this.lineSegPos = Math.max(0, Math.min(1, ((x - x1) * px + (y - y1) * py) / (this.distSqr1 = (px * px + py * py))));
return Math.sqrt(Math.pow((x1 + u * px) - x, 2) + Math.pow((y1 + u * py) - y, 2));
}
// function to get the distance of a point to a set of point describing a line
Helpers.prototype.getDistToPath = function (line,x,y) {
var i,len, lineLen,dist;
len = line.length;
x1 = line[0].x;
y1 = line[0].y;
var minDist = Infinity;
for(i = 1; i < len-1; i++){
var near = false;
x2 = line[i].x;
y2 = line[i].y;
lineLen = Math.hypot(x1-x2,y1-y2);
dist = Math.hypot((x1+x2)/2-x,(y1+y2)/2-y);
minDist = Math.min(minDist,dist);
if(dist < lineLen ){
minDist = Math.min(minDist,helpers.distPointToLine(x,y,x1,y1,x2,y2));
}
if(minDist < minDistToPass){
return minDist;
}
x1 = x2;
y1 = y2;
}
return minDist;
}
var helpers = new Helpers();
// Stuff for paths and drawing
var lineArray = []; // list of paths
var lastLine; // last line drawn
var points; // current recording path
var drawing = false; // flag is mouse down and drawing
var dragging = false;
var dragOffX;
var dragOffY;
var drawMode = "Draw";
var minDistToPass = 2; // If a line is closer than this then stop search we found the winning line
// functions to redraw all recorded lines
function redrawAll(){ // style to draw in
ctx.clearRect(0,0,canvas.width,canvas.height);
lineArray.each(function(p){ // draw each one point at atime
redraw(p,p.style);
})
}
// lineDesc is a line and its description
// style is a the style to draw the line in.
// withBox if true draw bounding box [optional]
function redraw(lineDesc,style,withBox){ // draws a single line with style
var line = lineDesc.line;
var len = line.length;
var i;
ctx.beginPath(); //
ctx.strokeStyle = style.colour; // set style and colour
ctx.lineWidth = lineDesc.style.width;
ctx.lineJoin = "round";
ctx.lineCap = "round";
ctx.moveTo(line[0].x,line[0].y); // move to the first pont
for(i = 1; i < line.length; i++){ // lineto all the rest
ctx.lineTo(line[i].x,line[i].y);
};
ctx.stroke(); // stroke
if(withBox){
var w = Math.ceil(lineDesc.style.width/2); // need the lines width to expand the bounding box
ctx.lineWidth = 1;
ctx.strokeStyle = "black";
ctx.strokeRect( // draw the box around the line
lineDesc.extent.minX-w,
lineDesc.extent.minY-w,
lineDesc.extent.width+w*2,
lineDesc.extent.height+w*2
)
}
// done
}
// Finds the closest line and returns it. If no line can be found it returns undefined.
function findLineAt(x,y){
var minDist = 20; // Set the cutoff limit. Lines further than this are ignored
var minLine;
var w;
lineArray.each(function(line){ // do ech line one at a time
w = line.style.width;
if(x >= line.extent.minX-w && x <= line.extent.maxX+w && // is the point inside the bounding
y >= line.extent.minY-w && y <= line.extent.maxY+w){ // boc
var dist = helpers.getDistToPath(line.line,x,y); // if so then do a detailed distance check
if(dist < minDist){ // is the distance to the line less than any other distance found
minDist = dist; // if so remember the line
minLine = line;
}
}
dist = Math.hypot(line.extent.centerX-x,line.extent.centerY-y); // check the distance to the
if(dist<minDist){ // center of the bounding boc
minDist = dist; // use this one if bound box center if close
minLine = line;
}
});
return minLine;
}
function refreshLine(line){ // updates the line to get extend and add stuff if needed
// a good place to smooth the line if need
if(!line.isLine){
var newLine = {}; // new object
newLine.isLine = true; // flag to indicate that the line has been updated
newLine.line = line; // attach the line
newLine.id = helpers.getID(); // get a unique Id for the line
newLine.style = { // give it a style
colour:helpers.randomColour(),
width:Math.random()*4+10,
};
}else{
var newLine = line;
}
var extent = helpers.extent.bind(helpers)
helpers.resetExtent();
line.each(extent);
newLine.extent = helpers.copyOfExtent();
return newLine;
}
function update(){ // one animframe
if(drawMode === "Draw"){
if(!mouse.lastButton && mouse.button ){ // if the mouse but just down;
points = []; // create an new array
drawing = true; // flag drawinf
lineArray.push(points); // save the point array onto the pointsArray
points.push({x:mouse.x,y:mouse.y}); // add the first point
setCursor("none");
}else
if(drawing && mouse.button){ // while the mouse is down keep drawing
points.push({x:mouse.x,y:mouse.y}); // save new point
var p1 = points[points.length-2]; // get last line seg and draw it
var p2 = points[points.length-1];
ctx.lineWidth = 1;
ctx.strokeStyle = "black";
ctx.beginPath();
ctx.moveTo(p1.x,p1.y);
ctx.lineTo(p2.x,p2.y);
ctx.stroke();
}else{
if(drawing){ // if drawing and mouse up
points.push({x:mouse.x,y:mouse.y}); // add the last point
lineArray.push(points = refreshLine(lineArray.pop()))
// redraw the newly draw line
redraw(points,points.style);
drawing = false; // flag that drawing is off.
}else{
setCursor("crosshair");
}
}
}else
if(drawMode = "Pick"){
if(!dragging && !mouse.button){ // is the mouse near a line and not dragging
ctx.clearRect(0,0,canvas.width,canvas.height); // clear background
ctx.drawImage(backGroundImage,0,0); // draw copy of existing lines
var line = findLineAt(mouse.x,mouse.y); // find the line
if(line !== undefined){ // is a line is near
setCursor("drag_small"); // highlight it
lastLine = line; // remember it
// draw it hightlighted with bounding box.
redraw(lastLine,{colour:helpers.occilatingColour(),width:lastLine.width},true);
}else{
setCursor(); // not near a line so turn of cursoe
}
}else // next check if the mouse has jsut been click to start a drag.
if(lastLine !== undefined && !mouse.lastButton && mouse.button){
if(mouse.which[2]){ // Check which button. Right? then copy
var newLine = [];
lastLine.line.each(function(p){newLine.push({x:p.x,y:p.y})});
newLine = refreshLine(newLine)
newLine.style = lastLine.style;
lastLine = newLine;
lineArray.push(newLine)
}else
if(mouse.which[1]){ // Check which button. Middle? then delete
var index;
lineArray.each(function(line,i){
if(line.id === lastLine.id){
index = i;
}
})
if(index !== undefined){
lineArray.splice(index,1);
}
ctx.clearRect(0,0,canvas.width,canvas.height);
redrawAll();
lastLine = undefined;
if(lineArray.length === 0){
drawMode = "Draw";
but.value = "Draw Mode";
}
}
if(lastLine !== undefined){
dragging = true;
dragOffX = mouse.x;
dragOffY = mouse.y;
// backGroundImage.ctx.clearRect(0,0,canvas.width,canvas.height);
// backGroundImage.ctx.drawImage(canvas,0,0);
}
}else{
if(dragging && !mouse.button){ // Drop is dragging true and not mouse down
dragging = false;
var ox = mouse.x-dragOffX; // Find the drag offset
var oy = mouse.y-dragOffY;
helpers.resetExtent(); // get ready for new bounding box.
lastLine.line.each(function(p){ // move each point of the line
p.x += ox;
p.y += oy;
helpers.extent(p); // and test the bounding box
return p;
})
lastLine.extent = helpers.copyOfExtent(); // get the new boundong box
ctx.clearRect(0,0,canvas.width,canvas.height);
redrawAll();
backGroundImage.ctx.clearRect(0,0,backGroundImage.width,backGroundImage.height);
backGroundImage.ctx.drawImage(canvas,0,0);
}else
if(dragging){ // if dragging
ctx.clearRect(0,0,canvas.width,canvas.height); // clear
ctx.drawImage(backGroundImage,0,0); // draw existing lines
var ox = mouse.x-dragOffX; // get the drag offset
var oy = mouse.y-dragOffY;
ctx.setTransform(1,0,0,1,ox,oy); // translate by drag offset
redraw(lastLine,lastLine.style); //draw the dragged line
ctx.setTransform(1,0,0,1,0,0); // reset transform
}
}
}
mouse.lastButton = mouse.button; // set the last button state
window.requestAnimationFrame(update); // request a new frame
}
window.requestAnimationFrame(update)
.canC {
width:256px;
height:256px;
border:black 2px solid;
}
.info{
font-size:x-small;
}
<input type="button" id="redrawAllID" value="Click to Pick"></input>
<div class="info">Mouse down to draw.In pick mode mouse hover over line.<br> Left Button drag,middle delete, right copy.</div>
<canvas class="canC" id="canV" width=256 height=256></canvas>
I want to add the vertical lines when I draw rectangle. The no of lines is dependent on the user and can be read from the text box.
I know the logic but somehow I am not able to get the answer.
I am calculating the width of the rectangle and then diving the width on the basis of no of vertical lines.
Click the checkbox near rectangle and draw using mouse down events
Please let me know where I am going wrong.
function PlotPitch()
{
var iPatches = document.getElementById('txtPatchCount').value;
var iTop = mySel.y;
var iBottom = mySel.y + mySel.h;
var iLeft = mySel.x;
var iX = iLeft;
canvas = document.getElementById('canvas2');
context = canvas.getContext('2d');
for (var iPatch=1; iPatch<iPatches; ++iPatch) {
iX = iLeft + iPatch*mySel.w/iPatches;
context.moveTo(iX, iTop);
context.lineTo(iX, iBottom);
}
context.lineWidth=0.25;
context.stroke();
}
http://jsfiddle.net/K5wcs/4/
If I am adding this the code is breaking and I am not able to draw anything.
What you should do if you have 'strange' behaviour is to separate concerns, so in this case that could be by creating a function that you test separately, which draws the lines, then once it's tested ok, plug it in code by just calling the function. You should find quickly.
So begin by testing this :
function drawLines(Context, mySel, iPatches) {
var iTop = mySel.y;
var iBottom = mySel.y + mySel.h;
var iLeft = mySel.x;
var iX = iLeft;
var colWidth = mySel.w/iPatches ;
for (var iPatch=1; iPatch<iPatches; ++iPatch) {
iX += colWidth;
Context.moveTo(iX, iTop);
Context.lineTo(iX, iBottom);
}
Context.lineWidth=0.25;
Context.stroke();
}
Good luck.
With the below code, position of a mesh is returned as (0, 0, 0) but it is not. So is the positioın vector calculated after render process?
me.scene.add(objMesh); //me is a project class
objMesh.updateMatrixWorld(true);
alert(objMesh.position.x + ',' + objMesh.position.y + ',' + objMesh.position.z);
objMesh is created from objfile, it is added to the scene correctly and centroid is approx (-8, 3, 0)
but position vector of objMesh is (0, 0, 0) do we have to auto calculate something first or should i calculate it manually from geometry vertices of the mesh ?
http://81.214.75.32:8181/admin is the url
the site is in Turkish so i will translate the UI items
in the site there is "Dosya" menu item
oppen the menu item and select "Proje Aç"
a dialog appears
in that dialog select MUTFAK_1
scene will appear
in that scene, every meshes position is (0, 0, 0)
is that possible :)
object.position is always local to the object. If you want to get the position in world space you need to get it from object.matrixWorld.
Try with this:
scene.add(objMesh);
scene.updateMatrixWorld(true);
var position = new THREE.Vector3();
position.getPositionFromMatrix( objMesh.matrixWorld );
alert(position.x + ',' + position.y + ',' + position.z);
r58
Update:
The function getPositionFromMatrix() has been renamed to setFromMatrixPosition().
For finding where in world space is the geometry centroid, try this:
objMesh.geometry.computeBoundingBox();
var boundingBox = objMesh.geometry.boundingBox;
var position = new THREE.Vector3();
position.subVectors( boundingBox.max, boundingBox.min );
position.multiplyScalar( 0.5 );
position.add( boundingBox.min );
position.applyMatrix4( objMesh.matrixWorld );
alert(position.x + ',' + position.y + ',' + position.z);
r58
Yeah. after some talk with mrdoob, i realized that .position of objects are local to theirselves. My situation was to find the center point of my mesh considering the vertices. Below is the code to get the centroid which came from an answer #447 ( https://github.com/mrdoob/three.js/issues/447 )
geom.centroid = new THREE.Vector3();
for (var i = 0, l = geom.vertices.length; i < l; i++) {
geom.centroid.addSelf(geom.vertices[i]);
}
geom.centroid.divideScalar(geom.vertices.length);
Now we have centroid of geometry...
Update
according to https://github.com/mrdoob/three.js/wiki/Migration, the .addSelf had been renamed to .add after r55
alert(objMesh.matrixWorld.getPosition().x + ',' + objMesh.matrixWorld.getPosition().y + ',' + objMesh.matrixWorld.getPosition().z);
You can use .getWorldPosition() method of a mesh, to get its absolute position.
Docs here: https://threejs.org/docs/#api/en/core/Object3D.getWorldPosition
According to this post the center of gravity C for a mesh can be found by
C = [sum of all (A*R)] / [sum of all A]
A = (area of a face * 2)
R = face centroid = average of vertices making the face
and here is the code in three.js
function calculateCenterOfMass( mesh ){
var centroid = new THREE.Vector3();
// centroid = centroidNominator / centroidDenominator;
var centroidNominator = new THREE.Vector3();
var centroidDenominator = 0;
for(var i = 0; i < mesh.geometry.faces.length; i++){
var Pi = mesh.geometry.faces[i].a;
var Qi = mesh.geometry.faces[i].b;
var Ri = mesh.geometry.faces[i].c;
var a = new THREE.Vector3(mesh.geometry.vertices[Pi].x, mesh.geometry.vertices[Pi].y, mesh.geometry.vertices[Pi].z);
var b = new THREE.Vector3(mesh.geometry.vertices[Qi].x, mesh.geometry.vertices[Qi].y, mesh.geometry.vertices[Qi].z);
var c = new THREE.Vector3(mesh.geometry.vertices[Ri].x, mesh.geometry.vertices[Ri].y, mesh.geometry.vertices[Ri].z);
var ab = b.clone().sub(a);
var ac = c.clone().sub(a);
var cross = new THREE.Vector3();
cross.crossVectors( ab, ac );
var faceArea = cross.lengthSq() / 2;
var faceCentroid = new THREE.Vector3( (a.x + b.x + c.x)/3, (a.y + b.y + c.y)/3, (a.z + b.z + c.z)/3 );
if (!isNaN(faceArea)){
centroidNominator.add(faceCentroid.multiplyScalar(faceArea));
centroidDenominator += faceArea;
}
}
centroid = centroidNominator.divideScalar(centroidDenominator);
return centroid;
}
Tip. If you are getting the position of a mesh within an imported model (e.g. GLTF) make sure that the mesh has it's origin set to it's own centre before exporting. i.e. don't apply transforms to it. Otherwise it's world centre will be the centre of the gltf and not the mesh itself.
I am working on a sort of itinerary mapper for packaged vacations, and I'm really happy with what I've done so far; I have the directions api implemented with a custom renderer, so I can take driving directions, and plot my own polyline complete with directional arrows that aren't google's awful ones spaced along the path. I am not exactly a math expert, and I am trying to figure out how I could make a path parallel to another path. For example, the itinerary goes from city 1 to city 2, and then back to city 1.
I want to offset the trip back to city 1's polyline, so that it mirrors the path, but travels parallel to it. Ideally, I would like to when I create the path, check for intersecting points in other paths, and if any are found, offset the path at those points only. This would be a better implementation, because you could for instance parallel the path only where it happens to intersect another one, like when it meets another path only for a short time.
I found this code for API2 from bill chadwick
The link is here: http://wtp2.appspot.com/ParallelLines.htm
Update: Somehow managed to convert this old v2 script to get it working in v3, but I'm experiencing some troubles...
It is more than doubling the original number of points, and following the path, but really throwing them in randomly. Screenshot here:
The class I converted is here:
function BDCCParallelLines(points, color, weight, opacity, opts, gapPx) {
console.log('Pllel COnstructor Initialized');
this.gapPx = gapPx;
this.points = points;
this.color = color;
this.weight = weight;
this.opacity = opacity;
this.opts = opts;
this.line1 = null;
this.line2 = null;
this.lstnZoom = null;
}
BDCCParallelLines.prototype = new google.maps.OverlayView();
BDCCParallelLines.prototype.onAdd = function() {
console.log('Pllel Initialized');
this.prj = map.getProjection();
var self = this;
this.lstnZoom = google.maps.event.addListener(map, "zoom_changed", function() {
self.recalc();
});
this.recalc();//first draw
}
BDCCParallelLines.prototype.onRemove = function() {
if(this.line2)
this.line2.setMap(null);
if(this.line1)
this.line1.setMap(null);
if(this.lstnZoom != null)
google.maps.event.removeListener(this.lstnZoom);
}
BDCCParallelLines.prototype.copy = function() {
return new BDCCParallelLines(this.points,this.color,this.weight,this.opacity,this.opts,this.gapPx);
}
BDCCParallelLines.prototype.draw = function(force) {
return; //do nothing
}
/**
* #param {google.maps.Map} map
* #param {google.maps.LatLng} latlng
* #param {int} z
* #return {google.maps.Point}
*/
BDCCParallelLines.prototype.latLngToPoint = function(latlng, z){
var normalizedPoint = map.getProjection().fromLatLngToPoint(latlng); // returns x,y normalized to 0~255
var scale = Math.pow(2, z);
var pixelCoordinate = new google.maps.Point(normalizedPoint.x * scale, normalizedPoint.y * scale);
return pixelCoordinate;
};
/**
* #param {google.maps.Map} map
* #param {google.maps.Point} point
* #param {int} z
* #return {google.maps.LatLng}
*/
BDCCParallelLines.prototype.pointToLatlng = function(point, z){
var scale = Math.pow(2, z);
var normalizedPoint = new google.maps.Point(point.x / scale, point.y / scale);
var latlng = map.getProjection().fromPointToLatLng(normalizedPoint);
return latlng;
};
BDCCParallelLines.prototype.recalc = function() {
var distallowance;
console.log('recalc called');
var zoom = map.getZoom();
distallowance = 1.6;
if(zoom > 6){
distallowance = 1.3;
if(zoom > 9){
distallowance = .7;
if( zoom > 13){
distallowance = .2;
if( zoom > 15){
distallowance = .0001;
}
}
}
}
console.log('Zoom Level: ' + zoom);
console.log('Allowance = ' + distallowance);
var pts1 = new Array();//left side of center
//shift the pts array away from the centre-line by half the gap + half the line width
var o = (this.gapPx + this.weight)/2;
var p2l,p2r;
for (var i=1; i<this.points.length; i++){
var p1lm1;
var p1rm1;
var p2lm1;
var p2rm1;
var thetam1;
var p1 = this.latLngToPoint(this.points[i-1], zoom)
var p2 = this.latLngToPoint(this.points[i], zoom)
var theta = Math.atan2(p1.x-p2.x,p1.y-p2.y);
theta = theta + (Math.PI/2);
var dl = Math.sqrt(((p1.x-p2.x)*(p1.x-p2.x))+((p1.y-p2.y)*(p1.y-p2.y)));
if(theta > Math.PI)
theta -= Math.PI*2;
var dx = Math.round(o * Math.sin(theta));
var dy = Math.round(o * Math.cos(theta));
var p1l = new google.maps.Point(p1.x+dx,p1.y+dy);
var p1r = new google.maps.Point(p1.x-dx,p1.y-dy);
p2l = new google.maps.Point(p2.x+dx,p2.y+dy);
p2r = new google.maps.Point(p2.x-dx,p2.y-dy);
if(i==1){ //first point
pts1.push(this.pointToLatlng(p1l,zoom));
}
else{ // mid this.points
if(distbetweentwo(this.points[i-1], this.points[i]) > distallowance){
if(theta == thetam1){
// adjacent segments in a straight line
pts1.push(this.pointToLatlng(p1l,zoom));
}
else{
var pli = this.intersect(p1lm1,p2lm1,p1l,p2l);
var pri = this.intersect(p1rm1,p2rm1,p1r,p2r);
var dlxi = (pli.x-p1.x);
var dlyi = (pli.y-p1.y);
var drxi = (pri.x-p1.x);
var dryi = (pri.y-p1.y);
var di = Math.sqrt((drxi*drxi)+(dryi*dryi));
var s = o / di;
var dTheta = theta - thetam1;
if(dTheta < (Math.PI*2))
dTheta += Math.PI*2;
if(dTheta > (Math.PI*2))
dTheta -= Math.PI*2;
if(dTheta < Math.PI){
//intersect point on outside bend
pts1.push(this.pointToLatlng(p2lm1,zoom));
pts1.push(this.pointToLatlng(new google.maps.Point(p1.x+(s*dlxi),p1.y+(s*dlyi)),zoom));
pts1.push(this.pointToLatlng(p1l,zoom));
}
else if (di < dl){
pts1.push(this.pointToLatlng(pli,zoom));
}
else{
pts1.push(this.pointToLatlng(p2lm1,zoom));
pts1.push(this.pointToLatlng(p1l,zoom));
}
}
}
else{
//console.log(distbetweentwo(this.points[i-1], this.points[i]));
}
}
p1lm1 = p1l;
p1rm1 = p1r;
p2lm1 = p2l;
p2rm1 = p2r;
thetam1 = theta;
//end loop
}
pts1.push(this.pointToLatlng(p2l,zoom));//final point
// console.log(pts1);
if(this.line1)
this.line1.setMap(null);
this.line1 = new google.maps.Polyline({
strokeColor: this.color,
strokeOpacity: this.opacity,
strokeWeight: this.weight,
map: map,
path: pts1 });
this.line1.setMap(map);
}
BDCCParallelLines.prototype.intersect = function(p0,p1,p2,p3)
{
// this function computes the intersection of the sent lines p0-p1 and p2-p3
// and returns the intersection point,
var a1,b1,c1, // constants of linear equations
a2,b2,c2,
det_inv, // the inverse of the determinant of the coefficient matrix
m1,m2; // the slopes of each line
var x0 = p0.x;
var y0 = p0.y;
var x1 = p1.x;
var y1 = p1.y;
var x2 = p2.x;
var y2 = p2.y;
var x3 = p3.x;
var y3 = p3.y;
// compute slopes, note the cludge for infinity, however, this will
// be close enough
if ((x1-x0)!=0)
m1 = (y1-y0)/(x1-x0);
else
m1 = 1e+10; // close enough to infinity
if ((x3-x2)!=0)
m2 = (y3-y2)/(x3-x2);
else
m2 = 1e+10; // close enough to infinity
// compute constants
a1 = m1;
a2 = m2;
b1 = -1;
b2 = -1;
c1 = (y0-m1*x0);
c2 = (y2-m2*x2);
// compute the inverse of the determinate
det_inv = 1/(a1*b2 - a2*b1);
// use Kramers rule to compute xi and yi
var xi=((b1*c2 - b2*c1)*det_inv);
var yi=((a2*c1 - a1*c2)*det_inv);
return new google.maps.Point(Math.round(xi),Math.round(yi));
}
This is working to a point... It is working as well as the original implementation. The entire path is recalculated on a zoom basis, and I kind of hacked the function to skip very short paths(weird angles) at higher zoom levels, it more closely follows the path the more you zoom in.
I would rather just have a fixed distance offset that is not recalculated, as it is pretty intensive... There are many programs which accomplish this feat, rhino3d, autocad, illustrator... I feel like it would be great for driving directions for google maps itself, an offsetting of the path so you can distinguish the return trip and the original trip.
If anybody has done anything similar to this in JS even if its not for google maps specifically, I would love to see it. Links I am investigating:
http://processingjs.nihongoresources.com/bezierinfo/
http://www.groupsrv.com/computers/about21532.html
Offsetting paths in general is a pretty tricky buisness. This paper (scientific paper alert) gives a good description of the steps taken for 'professional' offset algorithms.
http://cgcad.thss.tsinghua.edu.cn/~yongjh/papers/CiI2007V58N03P0240.pdf
You don't seem to want anything as fancy as in the demo. From what I gather you just want the same polyline, only shifted some pixels to the right and maybe some to the top so it doesn't overlap.
The code you posted has a latLngToPoint function and pointToLatLng function. I think the directions you get from Google are LatLng, so you can convert those to Points, increase the x and y property, and convert it back to a LatLng and draw your Polyline.
This should get you a line that exactly follows the original line. But, it wont look as fancy as the one in the demo. Since it wont be adding any points to smooth the line.