Fabricjs selection or hover based on mouse location on object bounding rectangle for inner objects - fabricjs

I want to achieve selection based on the bounding rectangle but with a different approach.
Scenario: If I draw object inside object, like first text, then rectangle over it, then ellipse and then triangle. Now I should be able to select the text or rectangle or ellipse OR the reverse order anyhow.
As I start hovering the triangle's bounding rect, the selection or active object should be triangle, but as I move my mouse over ellipse's bounding rect, the current object should be shown as ellipse and so on, irrespective of the order I have added the objects on canvas.
I tried with perPixelTargetFind and following solution Fabricjs - selection only via border, both the solutions are not working meeting my requirement.
I am using FabricJS version 3.6.3
Thanks in advance.

First you need to set perPixelTargetFind: true and targetFindTolerance:5.
Now you will face the issue for selection.
Issue: If you mousedown and drag on empty space, the object was getting selected.
Solution: Found a way to do that. I debugged through the fabric's mechanism to get objects on current mouse pointer location. There is a function _collectObjects which checks for intersectsWithRect (intersect with boundingRect points of the current object), isContainedWithinRect (do the points come inside the boundingRect), containsPoint(current mouse pointer points come in the current object location). So you need to override the _collectObjects function and remove containsPoint check. That will work.
Overridden function:
_collectObjects: function(e) {
var group = [],
currentObject,
x1 = this._groupSelector.ex,
y1 = this._groupSelector.ey,
x2 = x1 + this._groupSelector.left,
y2 = y1 + this._groupSelector.top,
selectionX1Y1 = new fabric.Point(min(x1, x2), min(y1, y2)),
selectionX2Y2 = new fabric.Point(max(x1, x2), max(y1, y2)),
allowIntersect = !this.selectionFullyContained,
isClick = x1 === x2 && y1 === y2;
// we iterate reverse order to collect top first in case of click.
for (var i = this._objects.length; i--; ) {
currentObject = this._objects[i];
if (!currentObject || !currentObject.selectable || !currentObject.visible) {
continue;
}
if ((allowIntersect && currentObject.intersectsWithRect(selectionX1Y1, selectionX2Y2)) ||
currentObject.isContainedWithinRect(selectionX1Y1, selectionX2Y2))
) {
group.push(currentObject);
// only add one object if it's a click
if (isClick) {
break;
}
}
}
if (group.length > 1) {
group = group.filter(function(object) {
return !object.onSelect({ e: e });
});
}
return group;
}

Related

How to center scaling of a JointJS paper

I've implemented the scaling of a paper and it works great.
I linked it to the scrolling of the mousewheel, but I still encounter one problem: In the API the scale function is defined as scale paper.scale(sx, sy, [ox, oy]) and I figured that ox and oy can center the zooming to a specific position. In my case this position should be the pointer. But although I hand over the coordinates (offsetX and offsetY of the mouse event), it has absolutly no effect.
Can someone give me an example of how to use ox and oy?
Don't forget to transform the mouse coordinates into the viewport coordinate system first.
function offsetToLocalPoint(offsetX, offsetY, paper) {
var svgPoint = paper.svg.createSVGPoint();
svgPoint.x = offsetX;
svgPoint.y = offsetY;
var offsetTransformed = svgPoint.matrixTransform(paper.viewport.getCTM().inverse());
return offsetTransformed
}
And reset the previous viewport translate transformation before you call paper.scale() with the origin [ox, oy] of scale transformation specified. You can see this in the mousewheel event handler example below.
function onMouseWheel(e) {
e.preventDefault();
e = e.originalEvent;
var delta = Math.max(-1, Math.min(1, e.wheelDelta)) / SPEED;
var newScale = V(paper.viewport).scale().sx + delta;
if (newScale > MIN_SCALE && newScale < MAX_SCALE) {
paper.setOrigin(0, 0); // reset the previous 'translate'
var p = offsetToLocalPoint(e.offsetX, e.offsetY);
paper.scale(newScale, newScale, p.x, p.y);
}
}
A cross-browser version fiddle here.

Arc menu item style like in tumblr app for Android

I tweaked https://github.com/daCapricorn/ArcMenu library to make it work from my purpose and that is working fine. Is there anyway I can build a menu like as in tumblr app or is it possible by tweaking the ArcMenu project or https://github.com/siyamed/android-satellite-menu ? I have doubts on the mathematical functions to be applied to create the style.
I am looking for a animation similar to tumblr app's animation.
try to use , Arc Menu
implement RayMenu and modify RayLayout
and modify the private static AnimationcreateExpandAnimation()` method with your specific coordinate.
There are many ways I do this in iOS. In iOS we have Views similar to that of Android and we can specify coordinates (frame of view) to place them in their superview.
Assumptions :
1)Assume you have a locus i.e circle with equation x^2 + y^2 = c
( Hint : All views are on this locus i.e center of all views coincide with this circle)
2) You need select a value of C depending on the curve you need and initial point which is
(0,HEIGHT_OF_SCREEN) as stating point for placing views.
Algorithm :
int angularDisplacementBetweenEachView = 10; // distance between two views in radians
// coordinates of X button in your screen
int startPositionOfViewX = 0;
int startPositionOfViewY = HEIGHT_OF_SCREEN;
// constant to decide curve
int C = 125;
-(void) main (){ // this is generally done in view controller
for(int i = 1; i< menu_items.count; i++){
Rect position = getCoordinatesForView(menu_item.get(i),menu_item.get(i-1).rect.x,menu_item.get(i-1).rect.y);
addView(menu_item.get(i),position.x,position.y);
}
}
// method returns the coordinates (x,y) at which the view is required to be
//placed.
-(Rect)getCoordinatesForView(View currentView, int prevViewX, int prevViewY){
// convert to polar cordinates
// refer diagram
// this is also the radius of the virtual circle
float r = sqrt(pow(x,2),pow(y,2));
float theta = atan(y,x);
//r & theta are polar coordinates of previous menu item
float theta2 = (angularDisplacementBetweenEachView + r*theta)/r;
// convert back to cartesian coordinates
int x2 = r * cos (theta);
int y2 = r * sin (theta);
return new rect(x2,y2);
}
try this for Arc menu. u can use this sattelite menu
https://github.com/ketanpatel25/android-satellite-menu-center

SVG: Calculating bounding box without displaying object

I need to get a text bounding box to adjust my layout before rendering anything. With some experimenting, I found that I had to actually render the text before 'getBBox' (or 'getComputedTextLength') will return a non-zero value:
var group = svgDocument.createElementNS(svgns, "g");
for(i=0; i <= nYblocks; ++i) {
str = svgDocument.createTextNode(strings[i]);
obj = tnode.cloneNode(true);
obj.setAttributeNS(null, "y", y1);
obj.appendChild(str);
group.appendChild(obj);
y1 += yBlockPx;
}
svgDocument.documentElement.appendChild(group); // **REQUIRED**
bb = vgroup.getBBox();
Problem: is there a good way to render the text so that it doesn't actually display? Should I just adjust the colours or opacity, or is there something clever I can do to render somewhere else, perhaps in a different tree?
Thanks -
Al
I think the easiest option is to draw it with the visibility set to hidden:
obj.setAttributeNS(null, "visibility", "hidden");

Using ShowTextAtPoint the displayed text is flipped

I'm using the ShowTextAtPoint method of CGContext to display a Text in a view, but it is displayed in flip mode, anyone knows how to solve this problem ?
Here is the code I use :
ctx.SelectFont("Arial", 16f, CGTextEncoding.MacRoman);
ctx.SetRGBFillColor(0f, 0f, 1f, 1f);
ctx.SetTextDrawingMode(CGTextDrawingMode.Fill);
ctx.ShowTextAtPoint(centerX, centerY, text);
You can manipulate the current transformation matrix on the graphics context to flip it using ScaleCTM and TranslateCTM.
According to the Quartz 2D Programming Guide - Text:
In iOS, you must apply a transform to the current graphics context in order for the text to be oriented as shown in Figure 16-1. This transform inverts the y-axis and translates the origin point to the bottom of the screen. Listing 16-2 shows you how to apply such transformations in the drawRect: method of an iOS view. This method then calls the same MyDrawText method from Listing 16-1 to achieve the same results.
The way this looks in MonoTouch:
public void DrawText(string text, float x, float y)
{
// the incomming coordinates are origin top left
y = Bounds.Height-y;
// push context
CGContext c = UIGraphics.GetCurrentContext();
c.SaveState();
// This technique requires inversion of the screen coordinates
// for ShowTextAtPoint
c.TranslateCTM(0, Bounds.Height);
c.ScaleCTM(1,-1);
// for debug purposes, draw crosshairs at the proper location
DrawMarker(x,y);
// Set the font drawing parameters
c.SelectFont("Helvetica-Bold", 12.0f, CGTextEncoding.MacRoman);
c.SetTextDrawingMode(CGTextDrawingMode.Fill);
c.SetFillColor(1,1,1,1);
// Draw the text
c.ShowTextAtPoint( x, y, text );
// Restore context
c.RestoreState();
}
A small utility function to draw crosshairs at the desired point:
public void DrawMarker(float x, float y)
{
float SZ = 20;
CGContext c = UIGraphics.GetCurrentContext();
c.BeginPath();
c.AddLines( new [] { new PointF(x-SZ,y), new PointF(x+SZ,y) });
c.AddLines( new [] { new PointF(x,y-SZ), new PointF(x,y+SZ) });
c.StrokePath();
}

Dirty Rectangles

Where may one find references on implementing an algorithm for calculating a "dirty rectangle" for minimizing frame buffer updates? A display model that permits arbitrary edits and computes the minimal set of "bit blit" operations required to update the display.
To build the smallest rectangle that contains all the areas that need to be repainted:
Start with a blank area (perhaps a rectangle set to 0,0,0,0 - something you can detect as 'no update required')
For each dirty area added:
Normalize the new area (i.e. ensure that left is less than right, top less than bottom)
If the dirty rectangle is currently empty, set it to the supplied area
Otherwise, set the left and top co-ordinates of the dirty rectangle to the smallest of {dirty,new}, and the right and bottom co-ordinates to the largest of {dirty,new}.
Windows, at least, maintains an update region of the changes that it's been informed of, and any repainting that needs to be done due to the window being obscured and revealed. A region is an object that is made up of many possibly discontinuous rectangles, polygons and ellipses. You tell Windows about a part of the screen that needs to be repainted by calling InvalidateRect - there is also an InvalidateRgn function for more complicated areas. If you choose to do some painting before the next WM_PAINT message arrives, and you want to exclude that from the dirty area, there are ValidateRect and ValidateRgn functions.
When you start painting with BeginPaint, you supply a PAINTSTRUCT that Windows fills with information about what needs to be painted. One of the members is the smallest rectangle that contains the invalid region. You can get the region itself using GetUpdateRgn (you must call this before BeginPaint, because BeginPaint marks the whole window as valid) if you want to minimize drawing when there are multiple small invalid areas.
I would assume that, as minimizing drawing was important on the Mac and on X when those environments were originally written, there are equivalent mechanisms for maintaining an update region.
Vexi is a reference implementation of this. The class is org.vexi.util.DirtyList (Apache License), and is used as part of production systems i.e. thoroughly tested, and is well commented.
A caveat, the currently class description is a bit inaccurate, "A general-purpose data structure for holding a list of rectangular regions that need to be repainted, with intelligent coalescing." Actually it does not currently do the coalescing. Therefore you can consider this a basic DirtyList implementation in that it only intersects dirty() requests to make sure there are no overlapping dirty regions.
The one nuance to this implementation is that, instead of using Rect or another similar region object, the regions are stored in an array of ints i.e. in blocks of 4 ints in a 1-dimensional array. This is done for run time efficiency although in retrospect I'm not sure whether there's much merit to this. (Yes, I implemented it.) It should be simple enough to substitute Rect for the array blocks in use.
The purpose of the class is to be fast. With Vexi, dirty may be called thousands of times per frame, so intersections of the dirty regions with the dirty request has to be as quick as possible. No more than 4 number comparisons are used to determine the relative position of two regions.
It is not entirely optimal due to the missing coalescing. Whilst it does ensure no overlaps between dirty/painted regions, you might end up with regions that line up and could be merged into a larger region - and therefore reducing the number of paint calls.
Code snippet. Full code online here.
public class DirtyList {
/** The dirty regions (each one is an int[4]). */
private int[] dirties = new int[10 * 4]; // gets grown dynamically
/** The number of dirty regions */
private int numdirties = 0;
...
/**
* Pseudonym for running a new dirty() request against the entire dirties list
* (x,y) represents the topleft coordinate and (w,h) the bottomright coordinate
*/
public final void dirty(int x, int y, int w, int h) { dirty(x, y, w, h, 0); }
/**
* Add a new rectangle to the dirty list; returns false if the
* region fell completely within an existing rectangle or set of
* rectangles (i.e. did not expand the dirty area)
*/
private void dirty(int x, int y, int w, int h, int ind) {
int _n;
if (w<x || h<y) {
return;
}
for (int i=ind; i<numdirties; i++) {
_n = 4*i;
// invalid dirties are marked with x=-1
if (dirties[_n]<0) {
continue;
}
int _x = dirties[_n];
int _y = dirties[_n+1];
int _w = dirties[_n+2];
int _h = dirties[_n+3];
if (x >= _w || y >= _h || w <= _x || h <= _y) {
// new region is outside of existing region
continue;
}
if (x < _x) {
// new region starts to the left of existing region
if (y < _y) {
// new region overlaps at least the top-left corner of existing region
if (w > _w) {
// new region overlaps entire width of existing region
if (h > _h) {
// new region contains existing region
dirties[_n] = -1;
continue;
}// else {
// new region contains top of existing region
dirties[_n+1] = h;
continue;
} else {
// new region overlaps to the left of existing region
if (h > _h) {
// new region contains left of existing region
dirties[_n] = w;
continue;
}// else {
// new region overlaps top-left corner of existing region
dirty(x, y, w, _y, i+1);
dirty(x, _y, _x, h, i+1);
return;
}
} else {
// new region starts within the vertical range of existing region
if (w > _w) {
// new region horizontally overlaps existing region
if (h > _h) {
// new region contains bottom of existing region
dirties[_n+3] = y;
continue;
}// else {
// new region overlaps to the left and right of existing region
dirty(x, y, _x, h, i+1);
dirty(_w, y, w, h, i+1);
return;
} else {
// new region ends within horizontal range of existing region
if (h > _h) {
// new region overlaps bottom-left corner of existing region
dirty(x, y, _x, h, i+1);
dirty(_x, _h, w, h, i+1);
return;
}// else {
// existing region contains right part of new region
w = _x;
continue;
}
}
} else {
// new region starts within the horizontal range of existing region
if (y < _y) {
// new region starts above existing region
if (w > _w) {
// new region overlaps at least top-right of existing region
if (h > _h) {
// new region contains the right of existing region
dirties[_n+2] = x;
continue;
}// else {
// new region overlaps top-right of existing region
dirty(x, y, w, _y, i+1);
dirty(_w, _y, w, h, i+1);
return;
} else {
// new region is horizontally contained within existing region
if (h > _h) {
// new region overlaps to the above and below of existing region
dirty(x, y, w, _y, i+1);
dirty(x, _h, w, h, i+1);
return;
}// else {
// existing region contains bottom part of new region
h = _y;
continue;
}
} else {
// new region starts within existing region
if (w > _w) {
// new region overlaps at least to the right of existing region
if (h > _h) {
// new region overlaps bottom-right corner of existing region
dirty(x, _h, w, h, i+1);
dirty(_w, y, w, _h, i+1);
return;
}// else {
// existing region contains left part of new region
x = _w;
continue;
} else {
// new region is horizontally contained within existing region
if (h > _h) {
// existing region contains top part of new region
y = _h;
continue;
}// else {
// new region is contained within existing region
return;
}
}
}
}
// region is valid; store it for rendering
_n = numdirties*4;
size(_n);
dirties[_n] = x;
dirties[_n+1] = y;
dirties[_n+2] = w;
dirties[_n+3] = h;
numdirties++;
}
...
}
It sounds like what you need is a bounding box for each shape that you're rendering to the screen. Remember that a bounding box of a polygon can be defined as a "lower left" (the minimum point) and an "upper right" (the maximum point). That is, the x-component of the minimum point is defined as the minimum of all the x-components of each point in a polygon. Use the same methodology for the y-component (in the case of 2D) and the maximal point of the bounding box.
If it's sufficient to have a bounding box (aka "dirty rectangle") per polygon, you're done. If you need an overall composite bounding box, the same algorithm applies, except you can just populate a single box with minimal and maximal points.
Now, if you're doing all this in Java, you can get your bounding box for an Area (which you can construct from any Shape) directly by using the getBound2D() method.
What language are you using? In Python, Pygame can do this for you. Use the RenderUpdates Group and some Sprite objects with image and rect attributes.
For example:
#!/usr/bin/env python
import pygame
class DirtyRectSprite(pygame.sprite.Sprite):
"""Sprite with image and rect attributes."""
def __init__(self, some_image, *groups):
pygame.sprite.Sprite.__init__(self, *groups)
self.image = pygame.image.load(some_image).convert()
self.rect = self.image.get_rect()
def update(self):
pass #do something here
def main():
screen = pygame.display.set_mode((640, 480))
background = pygame.image.load(open("some_bg_image.png")).convert()
render_group = pygame.sprite.RenderUpdates()
dirty_rect_sprite = DirtyRectSprite(open("some_image.png"))
render_group.add(dirty_rect_sprite)
while True:
dirty_rect_sprite.update()
render_group.clear(screen, background)
pygame.display.update(render_group.draw(screen))
If you're not using Python+Pygame, here's what I would do:
Make a Sprite class that's update(),
move() etc. method sets a "dirty"
flag.
Keep a rect for each sprite
If your API supports updating a list of rects, use that on the list of rects whose sprites are dirty. In SDL, this is SDL_UpdateRects.
If your API doesn't support updating a list of rects (I've never had the chance to use anything besides SDL so I wouldn't know), test to see if it's quicker to call the blit function multiple times or once with a big rect. I doubt that any API would be faster using one big rect, but again, I haven't used anything besides SDL.
I just recently wrote a Delphi class to calculate the difference rectangles of two images and was quite suprised by how fast it ran - fast enough to run in a short timer and after mouse/keyboard messages for recording screen activity.
The step by step gist of how it works is by:
Sub-dividing the image into logical 12x12 by rectangles.
Looping through each pixel and if there's a difference then I tell the sub-rectangle which the pixel belongs to that there's a difference in one of it's pixels and where.
Each sub-rectangle remembers the co-ordinates of it's own left-most, top-most, right-most and bottom-most difference.
Once all the differences have been found, I loop through all the sub-rectangles that have differences and form bigger rectangles out of them if they are next to each other and use the left-most, top-most, right-most and bottom-most differences of those sub-rectangles to make actual difference rectangles I use.
This seems to work quite well for me. If you haven't already implemented your own solution, let me know and I'll email you my code if you like. Also as of now, I'm a new user of StackOverflow so if you appreciate my answer then please vote it up. :)
Look into R-tree and quadtree data structures.

Resources