Context menu on canvas - html

I'm trying to use jQuery context menu to add a context menu on a specific element drawin in a html5 canvas, but I have no idea how i could bind it to that. Any tips?

Shapes drawn on html5 Canvas cannot be referenced
Any specific shape/path you've drawn on canvas is not remembered -- the shapes are just pixels on the canvas. So you can't bind your context menu to the un-referenceable shape.
So...You must manually find your shapes
As a starting point, you need to find your shape in the "sea of pixels".
Finding circular & rectangular shapes with Math
If the shape is rectangular or circular, you can mathematically find if the mouse-click is inside the shape using basic math.
// test if the mouse is inside a circle
// Given the circle's centerpoint and its radius
var dx = mouseX-circleCenterX;
var dy = mouseY-circleCenterY;
var mouseIsInCircle = (dx*dx+dy*dy)<(circleRadius*circleRadius);
// test if the mouse is inside a rectangle
// Given the rectangle's x,y,width & height
var mouseIsInRect=(
mouseX>=rect1.x &&
mouseX<=rect1.x+rect1.width &&
mouseY>=rect1.y &&
mouseY<=rect1.y+rect1.height);
Finding more complex paths with context.isPointInPath
One way to reference paths on the canvas is to put the commands that created the path in an object. When you need to hit-test that path to see if the context menu should deploy, you can redefine the path and use context.isPointInPath. This previous Stackoverflow Q&A shows how to use isPointInPath to test if the mouse is inside your complex path.
A better way with Path2D
Path2D is built into most modern browsers (but not IE) and it allows you to save a reference to your complex path. Then you can have context.isPointInPath use that Path2D definition to hit-test your Path2D.
The context menu itself
Here is a previous Stackoverflow Q&A that shows how to create a custom context menu.
If you want to use the jQuery context menu you will need to dynamically create on-demand menus and configure the menu to show at the desired [x,y] on the canvas. Some assembly is required

Related

How to change shape type in Google Slides?

I can see that Slides have getShapeType but don't have setShapeType method.
How may we udpate the shape type in Google Slides?
This is possible from the Google Slides context menu, but does not appear to be possible using Google App Script.
Any ideas? Thanks!
From Class Layout you can use getShapes to goto Class Shape where there are a lot methods for working with shapes.
Currently you cannot change the type of an existing Shape with Apps Script. This is not possible even with Slides API (you can change the shape properties in presentations.batchUpdate, but this doesn't include the shape type).
Once a Shape is created, the type is set, and if you want to have a Shape that has another type, you have to create a new one.
There are ways to achieve this, though, even if not so direct and fast:
Workaround #1 (Apps Script methods):
Using Apps Script built-in methods, you could do the following:
Get the properties of the shape you want to update (including position, rotation, scaling, color, etc.), with the corresponding methods (getRotation, getHeight, etc.).
Delete this existing shape via Shape.remove.
Create a new shape with the same properties as the old one, but with a different shape, via Slide.insertShape.
See for example, this sample, in which the position, rotation and dimensions of an existing shape is retrieved, then the shape is removed, and finally a new one is created with the same position, rotation and dimensions of the old one, and a triangular shape:
var left = shape.getLeft();
var top = shape.getTop();
var rotation = shape.getRotation();
var height = shape.getHeight();
var width = shape.getWidth();
shape.remove();
var shapeType = SlidesApp.ShapeType.TRIANGLE;
var newShape = slide.insertShape(shapeType, left, top, width, height);
newShape.setRotation(rotation);
Workaround #2 (Advanced Slides Service):
You could also activate Advanced Slides Service and use Slides API to accomplish the same process. You would have to do the following:
Retrieve the shape properties via presentations.get
Remove the old shape via presentations.batchUpdate, providing a DeleteObjectRequest.
Create a new shape with the properties of the old one and the new shape type via presentations.batchUpdate, providing a CreateShapeRequest.
Note:
I'd suggest you consider filing a Feature Request in this Issue Tracker component explaining the potential usefulness of this desired functionality.
Reference:
presentations.batchUpdate
UpdateShapePropertiesRequest
Advanced Slides Service
Slide.insertShape(shapeType, left, top, width, height)

How can you use HTML in the viewer, with the same scaling?

So, I am trying to take a different approach of adding text into the viewer by using HTML. My current approach is that I append the elements to the viewer container, but the problem with that is that the position is absolute. It always sticks to the screen/canvas, so when I move around in the scene, it does not move with it. What I want is for the element to be stuck to the scene, so when the camera moves, it moves along with the scene because it is stuck to it.
I have tried the following:
this.viewer.addEventListener(Autodesk.Viewing.GEOMETRY_LOADED_EVENT, function(event){
console.log(event.target);
event.target.impl.createOverlayScene('test');
event.target.impl.addOverlay('test');
var node = document.createElement("LI"); // Create a <li> node
var textnode = document.createTextNode("Water"); // Create a text node
node.appendChild(textnode);
// Append the text to <li>
event.target.clientContainer.appendChild(node)
});
I have messed around with overlay scenes, but it seems they are only for WebGL. So, what is the correct way to add elements to the viewer that share the same scale, and will move around with the camera like the element is part of the scene?
I have an extensive demo of that approach that you can find at Viewing.Extension.Markup3D. For more details see the article.
Keep in mind that will be a low-perf approach when dealing with a large number of markups compared with using a PointCloud + custom shader:
High-Performance 3D markups with PointCloud in the Forge Viewer

geotools draw symbol on overlay

I'm new to Geotools. I am developing a simple application that shows a map and I would like to dynamically place a bitmap or vector symbol on it (for example to show where the current position is, like in navigation systems).
Now, what I've done already is:
showing a map given one or more shapefile.
center the map on the last inserted position.
add shapes to new layers when needed.
What I need to do is to create an overlay with images at given coordinates on the map area (to be clear, I don't want to generate a raster layer on disk, I just want to paint on the screen).
My guess is that I have to somehow directly use the Graphics2D instance inside JMapPane, is that correct? In this case how can I convert from the geographic coordinates to pixel coordinates on the drawing pane? Or are there some geotools functions/classes that I should be looking for?
Thank you.
Answering myself, since I found the solution.
I did it by extending class org.geotools.map.DirectLayer, which provides a way to define a custom layer by extending its draw(Graphics2D, MapContent) method. Graphic elements are placed at the correct coordinates by using the worldToScreen affine transform provided by geotools. For example, to define a Point2D placed at the current position given real-world coordinates:
AffineTransform worldToScreen = viewport.getWorldToScreen();
Point2D worldPoint = new Point2D.Double(currentPosition.x, currentPosition.y);
Point2D screenPoint = worldToScreen.transform(worldPoint, null);
Once the draw() method is defined, just instantiate and add as a layer to your MapContent instance.

Canvas, negative coordinates: Is it bad to draw paths that start off canvas, and continue on?

I only want to show a portion of a shape drawn on a canvas.
My line is essentially this, and it works fine:
ctx.fillRect( xPosition, rectHeight - offsetV , rectWidth, rectHeight);
The second variable there is going to be negative. So, my quesiton is: is it bad practice (or am I setting myself for errors down the road) to draw a path that starts off the canvas (with a negative coordinate) and then continue drawing on to the canvas.
No problem at all. If you have very large number of drawing object you can (like GameAlchemist said) prevent drawing that object .If you use canvas like map for explore (zoom out/in ctx, translate whole context) that preventing draw can cost more that clip cost. And its complicated ...
I have some expire with drawing object out of canvas. You can have a problem if you put calculation and other (no drawing) staff intro draw function.
Important :
-Make canvas draw function code clear(only draw canvas code).
-If your app no need for const update make update call only when it needs.
-Clear canvas only in (0,0,canvas.w,canvas.h)
-Use style only when it needs (stroke,fill,font etc.)

Draw shapes on HTML5 Canvas...with video

I've been Googling around a bit for an answer and haven't found a definitive one either way: is it possible to play a video using an HTML5 canvas, and also allow the user to draw on this video? The use case, for some context, is to play a video on infinite loop so the user can draw multiple boxes over specific areas to indicate regions of interest.
As a bonus (:P), if I can figure out how to do this on its own, any hints as to how this could be done within Drupal? I'm already looking at the Canvas Field module, but if you have any hints on this point too (though the first one is the priority), that'd be awesome!
You can draw html5 video elements onto a canvas. The drawImage method accepts a video element in the first parameter just like an image element. This will take the current "frame" of the video element and render it onto the canvas. To get fluid playback of the video you will need to draw the video to the canvas repeatedly.
You can then draw on the canvas normally, making sure you redraw everything after each update of the video frame.
Here is a demo of video on canvas
here is a in-depth look into video and the canvas
I recently received this request from a client to provide this feature, and it must be CMS-friendly. The technique involves three big ideas
a drawing function
repeatedly calling upon the same drawing function
using requestAnimationFrame to paint the next frame
Assuming you have a video element already, you'd take the following steps
Hide the video element
Create a canvas element whose height/width match the video element, store this somewhere
Get the context of the canvas element with `canvas.getContext('2d') and also store that somewhere
Create a drawing function
In that drawing function, you would use canvas.drawImage(src, x, y) where src is the edited version of the current frame of the video;
In that drawing function, use recursion to call itself again
I can give you two examples of this being done (and usable for content management systems)
The first is here: https://jsfiddle.net/yywL381w/19/
A company called SDL makes a tool called Media Manager that hosts videos. What you see is a jQuery plugin that takes its parameters from a data-* , makes a request from the Media Manager Rest API, creates a video, and adds effects based entirely on data* attributes. That plugin could easily be tweaked to work with videos called from other sources. You can look at the repo for it for more details on usage.
Another example is here: http://codepen.io/paceaux/pen/egLOeR
That is not a jQuery plugin; it's an ES6 class instead. You can create an image/video and apply a cropping effect with this:
let imageModule = new ImageCanvasModule(module);
imageModule.createCanvas();
imageModule.drawOnCanvas();
imageModule.hideOriginal();
You'll observe, in the ImageCanvasModule class, this method:
drawFrame () {
if (this.isVideo && this.media.paused) return false;
let x = 0;
let width = this.media.offsetWidth;
let y = 0;
this.imageFrames[this.module.dataset.imageFrame](this.backContext);
this.backContext.drawImage(this.media, x, y, width, this.canvas.height);
this.context.drawImage(this.backCanvas, 0, 0);
if (this.isVideo) {
window.requestAnimationFrame(()=>{
this.drawFrame();
});
}
}
The class has created a second canvas, to use for drawing. That canvas isn't visible, it's just their to save the browser some heartache.
The "manipulation" that is content manageable is this.imageFrames[this.module.dataset.imageFrame](this.backContext);
The "frame" is an attribute stored on the image/video (Which could be output by a template in the CMS). This gets the name of the imageFrame, and runs it as a matching function. It also sends in the context (so I can toggle between drawing on the back canvas or main canvas if needed)
then this.backContext.drawImage(this.media, x, y, width, this.canvas.height); draws the image on the back context.
Finally, this appears on the main canvas with this.context.drawImage(this.backCanvas, 0, 0); where I take the backcanvas, and draw it on to the main canvas. So the canvas that's visible has the least amount of manipulations possible.
And at the end, because this is a video, we want to draw a new frame. So we have the function call itself:
if (this.isVideo) {
window.requestAnimationFrame(()=>{
this.drawFrame();
});
This whole setup allows us to use the CMS to output data-* attributes containing the type of frame the user wants to be drawn around the image. the JavaScript then produces a canvasified version of that image or video. Sample markup might look like:
<video muted loop autoplay data-image-frame="wedgeTop">