Identify each drawn path in Fabric JS - javascript

I am trying to identify each drawing.
For example after I draw an object I want that object to have the propriety name = "My Drawing 1" so I can easily refer to it later by
canvas.getObjectByName('My Drawing Name');
getObjectByName function
fabric.Canvas.prototype.getItemByName = function(name) {
var object = null, objects = this.getObjects();
for (var i = 0, len = this.size(); i < len; i++) {
if (objects[i].name && objects[i].name === name) {
object = objects[i];
break;
}
}
return object;
};
The code that activates drawing function:
canvas.isDrawingMode = true;
var img = $("#patternImg")[0];
var texturePatternBrush = new fabric.PatternBrush(canvas);
texturePatternBrush.source = img;
texturePatternBrush.selectable = true;
texturePatternBrush.name = img.getAttribute("data-name");
canvas.freeDrawingBrush = texturePatternBrush;
canvas.freeDrawingBrush.width = img.getAttribute("data-height");
Using the code above I can check if can verify if a drawing exists by:
fabric.Canvas.prototype.getDrawByName = function(name) {
var object = null, objects = this.getObjects();
for (var i = 0, len = this.size(); i < len; i++) {
if (objects[i].canvas.freeDrawingBrush.name && objects[i].canvas.freeDrawingBrush.name === name) {
object = objects[i];
break;
}
}
return object;
};
The problem is each time I am drawing an object that has another name canvas.FreeDrawingBrush.name will be the same for each object on my canvas.
I am not so familiar with FabricJS and I don't even know if what I am trying to do is even possible. At least I made myself clear what I am trying to do ?

When accessing the canvas property of a fabric.Object, you are simply getting a reference to the fabric.Canvas element that this object is rendered on. The freeDrawingBrush property of this fabric.Canvas element will always be the current free drawing brush.
Instead, I would set this name property onto each individual object, when it is done drawing. (This could be when you exit from drawing mode, or perhaps right before you switch to a different brush) using fabric.Object.set method.
Doing it this way, you aren't relying on an object's canvas property in any way.

Related

Create multiple instances of an object (by adding incremental numbers to it's name) and addChild to stage

I'm using Canvas/javascript (createjs) and am having difficulty calling an instance or adding a child to stage of a cloned shape (via an array using a for loop adding incremental numbers).
var myShape = new createjs.Shape();
myShape.graphics.f("white").rr(0, 0, 300, 300, 12);
myShape1 = myShape.clone();
myShape2 = myShape.clone();
myShape3 = myShape.clone();
//var arr = [null,cellFlasha1, cellFlasha2, cellFlasha3, cellFlasha4];
var arr = [];
for (var i = 1; i <= 4; i++) {
arr.push(["myShape"+i]);
}
stage.addChild(arr[1]);
I can't seem to add and instance to the stage. It does work when I use the array that has been commented out though. Could it be how i've combined a string and value when I push it to the array as an object?
I know I could just add it to stage by doing stage.addChild(myShape1); etc.. but I want to do it via a loop as there as there will be many more instances to come and similar scenarios (I intend to loop how I add the clones too so the number of objects can just be defined once)
I'm relatively new to javascript so my terminology may not be great. Many thanks in advance. Any help would be much appreciated!
Muzaffar is correct that you can access those variables via the window object, but it is generally a code smell to rely on globals for this kind of thing. Is all you need to get an arbitrary number of those shapes into an array? If so, why not try something like this?
function cloneShapeIntoArray(shape, num) {
var shapeArray = [];
for (var i = 0; i <= num; i++) {
shapeArray.push(shape.clone());
}
return shapeArray;
}
function addShapesToStage(shapes, stage) {
for (var i = 0; i <= shapes.length; i++) {
stage.addChild(shapes[i]);
}
}
var myShape = new createjs.Shape();
myShape.graphics.f("white").rr(0, 0, 300, 300, 12);
var shapes = cloneShapeIntoArray(myShape, 3);
// You can do some extra stuff to the shapes here, eg you could make each one a different scale
// shapes[0].scale = 1
// shapes[1].scale = 1.5
// shapes[2].scale = 2
addShapesToStage(shapes, stage);
That allows you to easily control how many copies you want, and does not pollute the global namespace.
Yes you can do this with "window" global object. Something like this
for(var i=1; i<=4; i++) {
window['myShape'+i] = myShape.clone();
}
var arr = [];
for (var i=1; i<= 4; i++) {
arr.push(window['myShape'+i]);
}
For more detail you can see here:
Use dynamic variable names in JavaScript

How to get location with dbid in 2D view

There is a simular question:
How to get location with dbid in 2D
but hasn't been anwsered.
So I have the same question, is there a way to get the object location info using dbid in 2D view?
We have a requirement in our product to mark certain object(s) and save the dbids in external database, and when next time opening the model, we need to use those ids and find the locations and then draw some custom shape to highlight those objects.
I tried to use viewer.impl.highlightObjectNode, but it can only display the object as it is selected, it's very limited on custom vitualization.
Here are some code snippets to access mesh information of Forge fragments, it might help you to find the location of a certain Forge viewer dbId. For more information, you can refer this extentsion: https://github.com/Autodesk-Forge/library-javascript-viewer-extensions/blob/master/src/Autodesk.ADN.Viewing.Extension.MeshData/Autodesk.ADN.Viewing.Extension.MeshData.js
Besides, viewer.impl.highlightObjectNode for highlighting elements only, it cannot be used in other purposes as my experince.
function getLeafFragIds( model, leafId ) {
const instanceTree = model.getData().instanceTree;
const fragIds = [];
instanceTree.enumNodeFragments( leafId, function( fragId ) {
fragIds.push( fragId );
});
return fragIds;
}
function getComponentGeometry( viewer, dbId ) {
const fragIds = getLeafFragIds( viewer.model, dbId );
let matrixWorld = null;
const meshes = fragIds.map( function( fragId ) {
const renderProxy = viewer.impl.getRenderProxy( viewer.model, fragId );
const geometry = renderProxy.geometry;
const attributes = geometry.attributes;
const positions = geometry.vb ? geometry.vb : attributes.position.array;
const indices = attributes.index.array || geometry.ib;
const stride = geometry.vb ? geometry.vbstride : 3;
const offsets = geometry.offsets;
matrixWorld = matrixWorld || renderProxy.matrixWorld.elements;
return {
positions,
indices,
offsets,
stride
};
});
return {
matrixWorld,
meshes
};
}
var meshInfo = getComponentGeometry( viewer, 1234 );
you can also check "viewer.impl.fitToView" in viewer3D.js, this function calculates selected objects union bounding box and fit them to view, so I extract code from this function to get object bounding box center coordinates with it's dbid
function getObjectBound2D(viewer, objectId) {
var model = viewer.model;
// This doesn't guarantee that an object tree will be created but it will be pretty likely
var bounds, bc, i;
if (model.is2d()) {
bounds = new THREE.Box3();
// move this next one up into the calling method
bc = new avp.BoundsCallback(bounds);
var dbId2fragId = model.getData().fragments.dbId2fragId;
var fragIds = dbId2fragId[objectId];
// fragId is either a single vertex buffer or an array of vertex buffers
if (Array.isArray(fragIds)) {
for (var j = 0; j < fragIds.length; j++) {
// go through each vertex buffer, looking for the object id
find2DBounds(model, fragIds[j], objectId, bc);
}
} else if (typeof fragIds === 'number') {
// go through the specific vertex buffer, looking for the object id
find2DBounds(model, fragIds, objectId, bc);
}
// should have some real box at this point; check
if (!bounds.empty()) {
return bounds;
}
}
function find2DBounds(model, fragId, dbId, bc) {
var mesh = model.getFragmentList().getVizmesh(fragId);
var vbr = new avp.VertexBufferReader(mesh.geometry);
vbr.enumGeomsForObject(dbId, bc);
}
function find2DLayerBounds(model, fragId, bc) {
var mesh = model.getFragmentList().getVizmesh(fragId);
var vbr = new avp.VertexBufferReader(mesh.geometry);
var visibleLayerIds = that.getVisibleLayerIds();
vbr.enumGeomsForVisibleLayer(visibleLayerIds, bc);
}
};
var objBoundingbox = getObjectBound2D(viewer,dbid);
var objCenterCoordinates = objBoundingbox.center();

For loop creating infinite object tree

I have 2 for loops creating an object:
function newImage(){
image = {};
var temp = {}
for(i=0;i!=250;i++){
temp[i] = {};
}
image = temp;
for(i=0;i!=250;i++){
image[i] = temp;
}
}
This should create an object with 250 values, each being an object that contains 250 objects. However, it creates an object that creates 250 values, fills those with 250 values, and loops this for a while. I haven't found the end of the tree, but it doesn't freeze leading me to believe that it is finite. I've checked the iterations up to 50 and it works all the way (it doesn't make the long tree). It seems as if it is happening during the last iterations. Here's the full thing.
var temp = {}
for(i=0;i!=250;i++){
temp[i] = {};
}
The lines above create an object and populate it with 250 other objects (so far so good).
Then image = temp; sets the image (global) variable to be temp (so image now contains 250 objects) then:
for(i=0;i!=250;i++){
image[i] = temp;
}
This replaces each of those 250 objects (overwriting the previous assignments) with a reference to the parent object so the object the has 250 attributes which all refer to itself.
Effectively what you wrote is:
image = {};
for ( var i = 0; i < 250; i++ )
image[i] = image;
It then appears that you have an infinite tree of objects whereas you only have a single object which has many attributes that refer to itself so whenever you descend to a child you end up back at the parent (and expanding the object hierarchy in the browser makes it appear to be an infinite tree when its actually only showing the same object over and over).
What you probably meant to write is:
function newImage(){
var temp = {};
for( var i=0; i < 250; i++){
temp[i] = {};
for( var j=0; j<250; j++){
temp[i][j] = {};
}
}
return temp;
}
Your problem is that when you assign image to temp, any further edits on either of those objects is reflected on the other object.
Try the following code instead:
function newImage(){
image = {};
var temp = {}
for(i=0;i!=250;i++){
temp[i] = {};
}
for(var item in temp){
image[item] = temp[item];
}
for(i=0;i!=250;i++){
image[i] = temp;
}
}

Unpredictable behaviour when setting a boolean variable in JavaScript [Deep copying in strict mode]

function computerSetManapool(cost) {
var splittedCost = cost.split(',');
for (var i = 0; i < splittedCost.length; i++) {
if (splittedCost[i] === "CL") {
for (var j = 0; j < computerLands.length; j++) {
if (!computerLands[j].tapped) {
computerLands[j].tapped = true;
console.log(computerLands[j].name + " gets tapped");
break;
}
}
}
}
}
I'm having a bit of trouble with this little piece of code, it's a Windows Store App, though I'm not using anything like the WinJS library or jQuery or what not.
Especially this line worries me:
computerLands[j].tapped = true;
I was debugging this in Visual Studio, while j had the value of 1. Therefore computerLands[1].tapped was supposed to be set to true.
Instead it set computerLands[1].tapped = true AND computerLands[3].tapped = true.
It does not happen every time, but many times and therefore I can't see what the problem is.
computerLands is an initially empty array, which then gets dynamically pushed objects into it.
If someone even has a remote idea what problem this could be, I would be really grateful.
Edit: This is the code where computerLands gets populated:
function computerTurn() {
untapAll("computer");
drawCards(1, "computer");
for (var i = 0; i < computerHand.length; i++) {
if (computerHand[i].type === "land") {
var container = document.getElementById("computerLandsContainer");
var newItem = document.createElement("div");
newItem.id = computerLandsID;
computerLandsID++;
newItem.style.backgroundImage = "url("+computerHand[i].image+")";
newItem.style.backgroundSize = "100%";
var btn1 = document.createElement("button");
btn1.id = "tapButtonComputer";
btn1.innerText = "˜";
newItem.appendChild(createEnlargeButton(newItem,"computer"));
newItem.appendChild(btn1);
container.appendChild(newItem);
computerLands.push(computerHand[i]);
console.log("Computer plays: " + computerHand[i].name);
computerLands[computerLands.length - 1].id = newItem.id;
var index = computerHand.indexOf(computerHand[i]);
computerHand.splice(index,1);
break;
}
}
}
The actual line is just this:
computerLands.push(computerHand[i]);
After pushing it into computerLands it gets removed from computerHand via splice().
var index = computerHand.indexOf(computerHand[i]);
computerHand.splice(index,1);
Finally I found a solution, I got it from this page: http://social.msdn.microsoft.com/Forums/windowsapps/en-US/f79b2e68-15a9-4d5e-8aad-e15f78d94840/how-do-i-clone-an-object?forum=winappswithhtml5
It describes how to deep copy in Javascript in Windows Store Apps (this other solution I found http://james.padolsey.com/javascript/deep-copying-of-objects-and-arrays/ is not allowed in Windows Store Apps strict mode), basically you create a new object and copy every property into it. Now here's my new working code:
function populateComputerLands() {
for (var i = 0; i < 4; i++) {
//computerLands.push(availableComputerCards[0]); <--- old code
var objectToCopy = availableComputerCards[0]; // new code
var newObject = Object.create(Object.getPrototypeOf(objectToCopy));
var newObjectProperties = Object.getOwnPropertyNames(objectToCopy);
var propertyName;
for (var p in newObjectProperties) {
propertyName = newObjectProperties[p];
Object.defineProperty(newObject, propertyName, Object.getOwnPropertyDescriptor(objectToCopy, propertyName));
};
computerLands.push(newObject);
}
}

Illustrator ExtendScript set FILL opacity of selection

Is there any way to access a pathItem's fill opacity with javascript? I can access the overall opacity, but I want to lower the opacity of the fill while keeping the stroke fully opaque.
I can't find anything in the documentation, nor can I find anyone else asking this question.
I can set the overall opacity like so:
var selection = app.activeDocument.selection;
selection[0].opacity = 50;
I've tried every variant of "fillOpacity" that I can think of, like this:
var selection = app.activeDocument.selection;
selection[0].fillOpacity = 50;
selection[0].FillOpacity = 50;
selection[0].fill.opacity = 50;
...but it doesn't work.
Am I going about this wrong, or is this just not possible?
You cannot access it, as you cannot access it normally even in illustrator. This is a Photoshop property only. I checked the documentation as well just to make sure. What you could do is this though and it would accomplish same thing:
doc = app.activeDocument;
i = 0
var selection = doc.selection[i];
var storedColor = doc.selection[i].fillColor;
//new object with only fill, we send it to back so it doesn't overlap stroke, if there is one
var newObject = app.selection[i].duplicate(doc, ElementPlacement.PLACEATEND);
//turn off fill for first object
doc.selection[i].filled = false;
i = i + 1;
newObject.stroked = false;
//apply stored color from earlier to new shape
newObject.fillColor = storedColor;
newObject.opacity = 50;
newObject.name = "50p fill";
What I did to solve the problem is to apply a spotcolor to the objects where I uses the tint property
var docRef = app.activeDocument;
var selectedObjects = docRef.selection;
var theTint;
var fillwithSwatch = function (pathItems, sname ){
for (var i=0;i< pathItems.length; i++){
pathItems[i].fill = true;
theTint = pathItems[i].fillColor.gray;
pathItems[i].fillColor = docRef.swatches.getByName ( sname ).color ;
pathItems[i].fillColor.tint = theTint;
}
}
theTint = fillTint(selectedObjects);
// the spotcolor should be in the swatchpallet already
fillwithSwatch (selectedObjects, "myBlue" );

Categories