When I set up text as a link, set a bounds shape, and set the hit area to the bounds shape, my hit area is off if the textAlign = 'center'. Any ideas?
var linkColor = "#0000ff";
var linkFont = "bold 14px Times";
var presentationLink = new createjs.Text("View Presentation", linkFont, linkColor);
presentationLink.textAlign = "left";
presentationLink.maxWidth = 170;
presentationLink.x = 300;
presentationLink.y = 125;
stage.addChild(presentationLink);
var plBounds = presentationLink.getTransformedBounds();
var s = new createjs.Shape().set({ x: plBounds.x, y: plBounds.y + plBounds.height });
s.graphics.s(linkColor).moveTo(0, 0).lineTo(plBounds.width, 0);
stage.addChild(s);
var hitAreaForPLink = new createjs.Shape(new createjs.Graphics().beginFill("#ffffff").drawRect(-10, -10, plBounds.width + 20, plBounds.height + 10));
presentationLink.hitArea = hitAreaForPLink;
stage.enableMouseOver();
presentationLink.on("mouseover", function () {
presentationLink.cursor = "pointer";
});
The hitArea is positioned according to its owner's coordinate system. That is, all of the owner's transformations are applied to the hitArea. This is so that if you were to animate the owner, the hitArea would track it as expected.
Since the transformation is already applied, you need to use getBounds, not getTransformedBounds. See this example: http://jsfiddle.net/gskinner/uagv5t84/2/
Related
I have been working with html canvas compositing trying to clip a pattern with a mask.
The main issue that I have is that the mask I have comes from an svg with transparencies within the outer most border. I want the entire inside from the outer most border to be filled with the pattern.
Take this SVG for example you can see that there is a single pixel border, then some transparency, and then an opaque red inner blob. The compositing I have done works as the documentation says it should, the single pixel border and the red inner portion pick up the pattern that I want to mask into this shape. The problem is that I want to mask the entire innards starting from the single pixel border.
This is where I think clip might help. But it seems clip only works with manually drawn paths, not paths from an svg (at least that I am aware of).
Is there a way to accomplish what I am trying to do?
Regards,
James
The Path2D constructor accepts an SVG path data argument, that it will parse as the d attribute of an SVG <path> element.
You can then use this Path2D object with the clip() method:
(async () => {
// fetch the svg's path-data
const markup = await fetch("https://upload.wikimedia.org/wikipedia/commons/7/76/Autism_spectrum_infinity_awareness_symbol.svg").then(resp => resp.ok && resp.text());
const doc = new DOMParser().parseFromString(markup, "image/svg+xml");
const pathData = doc.querySelector("[d]").getAttribute("d");
// build our Path2D object and use it
const path = new Path2D(pathData);
const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");
ctx.clip(path);
// draw something that will get clipped
const rad = 30;
for(let y = 0; y < canvas.height; y += rad * 2 ) {
for(let x = 0; x < canvas.width; x += rad * 2 ) {
ctx.moveTo(x+rad, y);
ctx.arc(x, y, rad, 0, Math.PI*2);
}
}
ctx.fillStyle = "red";
ctx.fill();
})().catch(console.error);
<canvas width="792" height="612"></canvas>
If you need to transform this path-data (e.g scale, or rotate), then you can create a second Path2D object, and use its .addPath(path, matrix) method to do so:
// same as above, but smaller
(async () => {
const markup = await fetch("https://upload.wikimedia.org/wikipedia/commons/7/76/Autism_spectrum_infinity_awareness_symbol.svg").then(resp => resp.ok && resp.text());
const doc = new DOMParser().parseFromString(markup, "image/svg+xml");
const pathData = doc.querySelector("[d]").getAttribute("d");
const originalPath = new Path2D(pathData);
const path = new Path2D();
// scale by 0.5
path.addPath(originalPath, { a: 0.5, d: 0.5 });
const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");
ctx.clip(path);
// draw something that will get clipped
const rad = 15;
for(let y = 0; y < canvas.height; y += rad * 2 ) {
for(let x = 0; x < canvas.width; x += rad * 2 ) {
ctx.moveTo(x+rad, y);
ctx.arc(x, y, rad, 0, Math.PI*2);
}
}
ctx.fillStyle = "red";
ctx.fill();
})().catch(console.error);
<canvas width="396" height="306"></canvas>
We can add plane geometry with this code and position in anywhere:
var geometry = new THREE.PlaneGeometry( 5, 20, 32 );
var material = new THREE.MeshBasicMaterial( {color: 0xffff00, side: THREE.DoubleSide} );
var plane = new THREE.Mesh( geometry, material );
scene.add( plane );
But how can we add plane when two diagonal positions are known. Is this possible?
The closest to this so far I have is to get center of two positions and it positions the plane correctly but does not size correctly:
var dir = pointB.clone().subtract(pointA);
var length = dir.length();
dir = dir.normalize().scale(length * 50);
center_position = pointA.clone().add(dir);
Direction is not important in my case, can be set by lookat().
Find the subtraction vector of those two, its absolute values are plane size.
Average vector of those two is plane's center.
Example (r120):
body{
overflow: hidden;
margin: 0;
}
<script type="module">
import * as THREE from "https://threejs.org/build/three.module.js";
import { OrbitControls } from "https://threejs.org/examples/jsm/controls/OrbitControls.js";
let scene = new THREE.Scene();
let camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 1, 100);
camera.position.set(0, 5, 10);
let renderer = new THREE.WebGLRenderer();
renderer.setSize(innerWidth, innerHeight);
document.body.appendChild(renderer.domElement);
let controls = new OrbitControls(camera, renderer.domElement);
scene.add(new THREE.GridHelper());
let vec1 = new THREE.Vector3(0, 2, 0);
let vec2 = new THREE.Vector3(4, 0, 0);
let size = new THREE.Vector3().subVectors(vec2, vec1);
let center = new THREE.Vector3().addVectors(vec1, vec2).multiplyScalar(0.5);
let planeWidth = Math.abs(size.x);
let planeHeight = Math.abs(size.y);
let planeGeom = new THREE.PlaneBufferGeometry(planeWidth, planeHeight, planeWidth, planeHeight);
let planeMat = new THREE.MeshBasicMaterial({color: "aqua", wireframe: true});
let plane = new THREE.Mesh(planeGeom, planeMat);
plane.position.copy(center);
scene.add(plane);
renderer.setAnimationLoop(()=>{
renderer.render(scene, camera);
});
</script>
I have a simple 3D object and I am able to rotate i.e., move with my left mouse button, around the centre of axis - works fine. When I pan using the right mouse button the axis also shifts, as such it no longer moves around it’s present axis.
How can I move the object around it’s current axis, no matter where I drag the object?
Below is the complete code of script.js
var scene = new THREE.Scene();
var axisHelper = new THREE.AxisHelper(100);
scene.add(axisHelper);
var camera = new THREE.PerspectiveCamera( 75, window.innerWidth/window.innerHeight, 0.1, 1000 );
camera.position.y = -200;
var renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
var controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.25;
controls.enableZoom = true;
var keyLight = new THREE.DirectionalLight(new THREE.Color('hsl(30, 100%, 75%)'), 1.0);
keyLight.position.set(-100, 0, 100);
var fillLight = new THREE.DirectionalLight(new THREE.Color('hsl(240, 100%, 75%)'), 0.75);
fillLight.position.set(100, 0, 100);
var backLight = new THREE.DirectionalLight(0xffffff, 1.0);
backLight.position.set(100, 0, -100).normalize();
scene.add(keyLight);
scene.add(fillLight);
scene.add(backLight);
var mtlLoader = new THREE.MTLLoader();
mtlLoader.setTexturePath('assets/');
mtlLoader.setPath('assets/');
mtlLoader.load('180319_object01.mtl', function (materials) {
materials.preload();
var objLoader = new THREE.OBJLoader();
objLoader.setMaterials(materials);
objLoader.setPath('assets/');
objLoader.load('180319_object01.obj', function (object) {
object.scale.set( 200, 200, 200 );
scene.add(object);
object.position.x = -100;
object.position.y = -100;
object.position.z = 0;
});
});
var animate = function () {
requestAnimationFrame( animate );
controls.update();
renderer.render(scene, camera);
};
animate();
It is because your camera is being moved by the controller and not the object itself, rather than update the cameras position and direction ((ie which is what the orbit controller is doing).in your render method you'll want to update position and Euler angle on the object itself to achieve the desired effect. To do this you'll want to track and update the position and angle of rotation about the (y?) Axis of the object,in the object (model) space.hope that makes sense, let me know if you need me to elaborate.
My objective in this jsfiddle is to have a Fabric object (for example, a rect) that will "visually" keep its original border width even when it is scaled/expanded. For example, let's say that strokeWidth = 3px; usually when a rect is expanded the user will see the border wider. In my case I need the border to keep its width in screen pixels.
To achieve that, I calculate a factor factorHeight = origHeight / obj.getHeight() every time the object is scaled, and multiply strokeWidth by this factor. This works well when the object is scaled proportionally (try it in the jsfiddle).
However, when the object is not scaled proportionally, the problem is that the top/bottom border widths are different from the right/left border widths. Since there's a single strokeWidth for all these borders I cannot change this behavior. Any ideas?
Javascript:
var canvas = new fabric.Canvas('c' );
var origWidth = 40;
var origHeight = 100;
var origStrokeWidth = 3;
var rect = new fabric.Rect({
top: 30,
left: 30,
width: origWidth,
height: origHeight,
fill: 'rgba(255,255,255,0)',
strokeWidth: origStrokeWidth,
stroke: '#000000'
});
canvas.add(rect);
canvas.on('object:scaling', onObjectScaled);
function onObjectScaled(e) {
var obj = e.target;
var factorHeight = origHeight / obj.getHeight();
var factorWidth = origWidth / obj.getWidth();
var factor;
if (factorHeight < factorWidth)
factor = factorHeight;
else
factor = factorWidth;
var newStrokeWidth = origStrokeWidth * factor;
obj.setStrokeWidth(newStrokeWidth);
canvas.renderAll();
}
I had the same problem, this is the solution I found somewhere here on SO, but I cant provide you with the link to question, but here is the code that solved it:
canvas.on({
'object:scaling': function(e) {
var obj = e.target;
obj.KeepStrokeWidth = 5;
if(obj.KeepStrokeWidth){
var newStrokeWidth = obj.KeepStrokeWidth / ((obj.scaleX + obj.scaleY) / 2);
obj.set('strokeWidth',newStrokeWidth);
}
}
});
Hope it will work for you too.
I have a LineString and some points on it as Polygon. When I click on the yellow line, than the green polygon gets rendered behind the line. I would like to disable the click on the line.
Here is a picture about my problem
http://i57.tinypic.com/2rc3kg3.png
Here is a code snipet:
function PointDrawing(Lon, Lat, VectorLayer)
{
var circlreFeatureStyle = new OpenLayers.Style();
circlreFeatureStyle.fillColor = '#009900';
circlreFeatureStyle.fillOpacity =0.8
circlreFeatureStyle.strokeColor = '#004F00';
var circle = OpenLayers.Geometry.Polygon.createRegularPolygon(
new OpenLayers.Geometry.Point(Lon, Lat),
(1 * 15),
30
);
circleFeature = new OpenLayers.Feature.Vector(circle);
circleFeature.style = circlreFeatureStyle;
VectorLayer.addFeatures(circleFeature);
}
function LineDrawing(Map,VectorLayer)
{
var line = new OpenLayers.Geometry.LineString(points);
var style = {
strokeColor: 'yellow',
strokeOpacity: 4.5,
strokeWidth: 5
};
var lineFeature = new OpenLayers.Feature.Vector(line, null, style);
VectorLayer.removeAllFeatures();
VectorLayer.addFeatures([lineFeature]);
}