How to normal-scale using threejs to make objects 'thicker' or 'thinner' - math

I'm trying to figure out how to extrude an already loaded .obj object to make it "thicker". I think I'm looking for a way to scale my object not from its anchor point but scaling it by each polygon normal.
A classic example would be to take a "ring" object. If you scale it up with just the normal scale methods it just gets bigger from the center but I want the ring to become thicker/thinner. Its called a 'normal scale' in cinema 4d.
Here's some example code of what I currently have, and which isn't giving me the expected result.
var objLoader = new THREE.OBJLoader();
var material = new THREE.MeshLambertMaterial({color:'yellow', shading:THREE.FlatShading});
objLoader.load('objects/gun/M1911.obj', function (obj) {
obj.traverse(function (child) {
if (child instanceof THREE.Mesh) {
child.geometry.computeVertexNormals();
child.material = material;
}
});
obj.material = material;
obj.scale.set(7, 7, 7);
scene.add(obj);
});

scaleGeo: function (geo, ratio) {
for (var i = 0; i < geo.faces.length; i++) {
var faceI = geo.faces[i];
var vertexArr = [faceI.a, faceI.b, faceI.c];
for (var j = 0; j < vertexArr.length; j++) {
var vertexJ = geo.vertices[vertexArr[j]];
var normalJ = faceI.vertexNormals[j];
if (vertexJ.hasScale) continue;
vertexJ.x += normalJ.x * ratio;
vertexJ.y += normalJ.y * ratio;
vertexJ.z += normalJ.z * ratio;
vertexJ.hasScale = true;
}
}
return geo;
},

Related

Select symbol definition path

I need to view the segments and handles of the path that defines a SymbolItem. It is a related issue to this one but in reverse (I want the behavior displayed on that jsfiddle).
As per the following example, I can view the bounding box of the SymbolItem, but I cannot select the path itself in order to view its segments/handles. What am I missing?
function onMouseDown(event) {
project.activeLayer.selected = false;
// Check whether there is something on that position already. If there isn't:
// Add a circle centered around the position of the mouse:
if (event.item === null) {
var circle = new Path.Circle(new Point(0, 0), 10);
circle.fillColor = '#' + Math.floor(Math.random() * 16777215).toString(16);
var circleSymbol = new SymbolDefinition(circle);
multiply(circleSymbol, event.point);
}
// If there is an item at that position, select the item.
else {
event.item.selected = true;
}
}
function multiply(item, location) {
for (var i = 0; i < 10; i++) {
var next = item.place(location);
next.position.x = next.position.x + 20 * i;
}
}
Using SymbolDefinition/SymbolItem prevent you from changing properties of each symbol items.
The only thing you can do in this case is select all symbols which share a common definition.
To achieve what you want, you have to use Path directly.
Here is a sketch showing the solution.
function onMouseDown(event) {
project.activeLayer.selected = false;
if (event.item === null) {
var circle = new Path.Circle(new Point(0, 0), 10);
circle.fillColor = Color.random();
// just pass the circle instead of making a symbol definition
multiply(circle, event.point);
}
else {
event.item.selected = true;
}
}
function multiply(item, location) {
for (var i = 0; i < 10; i++) {
// use passed item for first iteration, then use a clone
var next = i === 0 ? item : item.clone();
next.position = location + [20 * i, 0];
}
}

Circle mask acts like rectangle

I'm trying to draw an object masked with a circle, but the mask applied is more like a circle's bounding rectangle
I tried using circle drawn with drawCircle method:
private function squareToRound(ability:MovieClip):MovieClip
{
var container:MovieClip = new MovieClip();
var newAbility:MovieClip = ability;
var newMask:MovieClip = new MovieClip();
newAbility.width = 43;
newAbility.height = 43;
newMask.x = newAbility.x + newAbility.width / 2;
newMask.y = newAbility.y + newAbility.height / 2;
container.addChild(newAbility);
newMask.graphics.beginFill();
newMask.graphics.drawCircle(0, 0, 8);
newMask.graphics.endFill();
container.addChild(newMask);
newAbility.mask = newMask;
return container;
}
... and as an asset from SWF:
private function squareToRound(ability:MovieClip):MovieClip
{
var container:MovieClip = new MovieClip();
var newAbility:MovieClip = ability;
var newMask:MovieClip = new CircleAbilityMask();
newAbility.width = 43;
newAbility.height = 43;
newMask.x = newAbility.x + newAbility.width / 2;
newMask.y = newAbility.y + newAbility.height / 2;
container.addChild(newAbility);
container.addChild(newMask);
newAbility.mask = newMask;
return container;
}
The results are equal.
I checked what the mask looks like by commenting "newAbility.mask = newMask;" line
This should be resolved in the current OpenFL release:
https://github.com/openfl/openfl/blob/develop/CHANGELOG.md#650-11102017
You can also use Cairo (on native platforms) or Canvas (on HTML5) for smoother/better masking support:
openfl test html5 -Dcanvas
openfl test windows -Dcairo
These are also used when you bitmapData.draw, sprite.cacheAsBitmap = true or other APIs that trigger a software render rather than GL.

creating a Placemarks that can be hidden

I have been trying to create a Placemark that I can hide and show (like turning visibility on and off) on demand (on click)... I am using this to make the placemark:
function placemark(lat, long, name, url, iconsrc){
var placemark = ge.createPlacemark(name);
ge.getFeatures().appendChild(placemark);
placemark.setName(name);
// Create style map for placemark
var icon = ge.createIcon('');
if(iconsrc == "0")
icon.setHref('http://maps.google.com/mapfiles/kml/paddle/red-circle.png');
else{
icon.setHref(iconsrc);
}
var style = ge.createStyle('');
style.getIconStyle().setIcon(icon);
if(iconsrc != "0")
style.getIconStyle().setScale(2.5);
placemark.setStyleSelector(style);
// Create point
var point = ge.createPoint('');
point.setLatitude(lat);
point.setLongitude(long);
//point.setAltitudeMode(1500);
placemark.setGeometry(point);
google.earth.addEventListener(placemark, 'click', function(event) {
// Prevent the default balloon from popping up.
event.preventDefault();
var balloon = ge.createHtmlStringBalloon('');
balloon.setFeature(placemark); // optional
balloon.setContentString(
'<iframe src="'+ url +'" frameborder="0"></iframe>');
ge.setBalloon(balloon);
});
}
I have tried everything... from this:
function hidePlacemark(name){
var children = ge.getFeatures().getChildNodes();
for(var i = 0; i < children.getLength(); i++) {
var child = children.item(i);
if(child.getType() == 'KmlPlacemark') {
if(child.getId()== name)
child.setVisibility(false);
}
}
}
to using this ge.getFeatures().removeChild(child);
can anyone point me to the right direction on creating a function that will allow me to turn the visibility on/off on demand please.
Your hidePlacemark function is missing some {} in your final IF statement
if(child.getId()== name)
you have
function hidePlacemark(name){
var children = ge.getFeatures().getChildNodes();
for(var i = 0; i < children.getLength(); i++) {
var child = children.item(i);
if(child.getType() == 'KmlPlacemark') {
if(child.getId()== name)
child.setVisibility(false);
}
}
}
make it
function hidePlacemark(name){
var children = ge.getFeatures().getChildNodes();
for(var i = 0; i < children.getLength(); i++) {
var child = children.item(i);
if(child.getType() == 'KmlPlacemark') {
if(child.getId()== name) {
child.setVisibility(false);
}
}
}
}
HOWEVER ------- you are better off doing this as it is much faster as you don't need to loop through ALL your placemarks
function hidePlacemark(name) {
var placemark = ge.getElementById(name);
placemark.setVisibility(false);
}
I think the plain ge.getFeatures().removeChild(placemark); works.
I played with this GooglePlayground, and just added the following code to line 8 (that is empty in this GooglePlayground Sample):
addSampleButton('Hide Placemark', function(){
ge.getFeatures().removeChild(placemark);
});
Clicking the button Hide Placemark hides the placemark like a charm here. Any chances your problem is somewhere else in your code?

Changing stroke attribute on a single RaphaelJS object, when there are multiple objects on the page

I've got a whole bunch of rects on my canvas.
I'd like to change the stroke on whatever rect the user clicks, as well as running some other javascript. My simplified code is below.
var canvas = Raphael("test");
var st = canvas.set();
for (var i = 0; i < 2; i++) {
var act = canvas.rect(///edited for brevity////).attr({"stroke":"none"});
st.push(act)
act.node.onclick = function() {
st.attr({stroke: "none"});
act.attr({stroke: "yellow"});
}
}
Right now, no matter what rect I click on, it's only changing the stroke on the last rect drawn.
Any ideas?
Not a Raphaƫl problem but rather lack of closure understanding. Easily could be fixed by self invoking function:
for (var i = 0; i < 2; i++) {
var act = canvas.rect(///edited for brevity////).attr({"stroke":"none"});
st.push(act)
(function (act) {
act.node.onclick = function() {
st.attr({stroke: "none"});
act.attr({stroke: "yellow"});
}
})(act);
}
//Try and then embellish
st[i].click(function (e)
{
this.attr({stroke: "yellow"});
}

Get bounds of filters applied to Flash Sprite within Sprite

I have a Flash library with Sprite symbols composed of other sprites with design-time applied filters. I'm embedding those symbols into a Flex application like so:
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script>
<![CDATA[
[Bindable]
[Embed(source="Resources.swf", symbol="SquareContainer")]
private var squareContainer_class:Class;
private function log(msg:String):void {
output.text = output.text + "\n" + msg;
}
]]>
</mx:Script>
<mx:VBox horizontalAlign="center" width="100%" height="100%" >
<mx:Image id="squareContainer" source="{squareContainer_class}"/>
<mx:Button click="log(squareContainer.width + ', ' + squareContainer.height);"/>
<mx:TextArea id="output" width="100%" height="100%" />
</mx:VBox>
</mx:Application>
In this example, the SquareContainer symbol is 100px wide by 100px height; however it contains a child sprite with a glow and blur filter, that cause the sprite to appear to be significantly larger than 100x100. Since I cannot know for certain the composition of the container, I cannot use BitmapData.generateFilterRect() to get at the filters applied to nested sprites.
How can I get the size of the sprite plus its filters?
Oh sweet success! (and thanks for the tips) A friend helped solve the problem with a nice recursive function to handle the filters which may exist on nested sprites:
private function getDisplayObjectRectangle(container:DisplayObjectContainer, processFilters:Boolean):Rectangle {
var final_rectangle:Rectangle = processDisplayObjectContainer(container, processFilters);
// translate to local
var local_point:Point = container.globalToLocal(new Point(final_rectangle.x, final_rectangle.y));
final_rectangle = new Rectangle(local_point.x, local_point.y, final_rectangle.width, final_rectangle.height);
return final_rectangle;
}
private function processDisplayObjectContainer(container:DisplayObjectContainer, processFilters:Boolean):Rectangle {
var result_rectangle:Rectangle = null;
// Process if container exists
if (container != null) {
var index:int = 0;
var displayObject:DisplayObject;
// Process each child DisplayObject
for(var childIndex:int = 0; childIndex < container.numChildren; childIndex++){
displayObject = container.getChildAt(childIndex);
//If we are recursing all children, we also get the rectangle of children within these children.
if (displayObject is DisplayObjectContainer) {
// Let's drill into the structure till we find the deepest DisplayObject
var displayObject_rectangle:Rectangle = processDisplayObjectContainer(displayObject as DisplayObjectContainer, processFilters);
// Now, stepping out, uniting the result creates a rectangle that surrounds siblings
if (result_rectangle == null) {
result_rectangle = displayObject_rectangle.clone();
} else {
result_rectangle = result_rectangle.union(displayObject_rectangle);
}
}
}
// Get bounds of current container, at this point we're stepping out of the nested DisplayObjects
var container_rectangle:Rectangle = container.getBounds(container.stage);
if (result_rectangle == null) {
result_rectangle = container_rectangle.clone();
} else {
result_rectangle = result_rectangle.union(container_rectangle);
}
// Include all filters if requested and they exist
if ((processFilters == true) && (container.filters.length > 0)) {
var filterGenerater_rectangle:Rectangle = new Rectangle(0,0,result_rectangle.width, result_rectangle.height);
var bmd:BitmapData = new BitmapData(result_rectangle.width, result_rectangle.height, true, 0x00000000);
var filter_minimumX:Number = 0;
var filter_minimumY:Number = 0;
var filtersLength:int = container.filters.length;
for (var filtersIndex:int = 0; filtersIndex < filtersLength; filtersIndex++) {
var filter:BitmapFilter = container.filters[filtersIndex];
var filter_rectangle:Rectangle = bmd.generateFilterRect(filterGenerater_rectangle, filter);
filter_minimumX = filter_minimumX + filter_rectangle.x;
filter_minimumY = filter_minimumY + filter_rectangle.y;
filterGenerater_rectangle = filter_rectangle.clone();
filterGenerater_rectangle.x = 0;
filterGenerater_rectangle.y = 0;
bmd = new BitmapData(filterGenerater_rectangle.width, filterGenerater_rectangle.height, true, 0x00000000);
}
// Reposition filter_rectangle back to global coordinates
filter_rectangle.x = result_rectangle.x + filter_minimumX;
filter_rectangle.y = result_rectangle.y + filter_minimumY;
result_rectangle = filter_rectangle.clone();
}
} else {
throw new Error("No displayobject was passed as an argument");
}
return result_rectangle;
}
Here is a slightly different approach: just draw the entire object into BitmapData and then calculate the bounds of the non-transparent area of the bitmap. This approach might be more performant, especially on complex objects.
package lup.utils
{
import flash.display.BitmapData;
import flash.display.DisplayObject;
import flash.geom.Matrix;
import flash.geom.Point;
import flash.geom.Rectangle;
public class BoundsHelper
{
private var _hBmd:BitmapData;
private var _hBmdRect:Rectangle;
private var _hMtr:Matrix;
private var _hPoint:Point;
private var _xMin:Number;
private var _xMax:Number;
private var _yMin:Number;
private var _yMax:Number;
/**
* Specify maxFilteredObjWidth and maxFilteredObjHeight to match the maximum possible size
* of a filtered object. Performance of the helper is inversely proportional to the product
* of these values.
*
* #param maxFilteredObjWidth Maximum width of a filtered object.
* #param maxFilteredObjHeight Maximum height of a filtered object.
*/
public function BoundsHelper(maxFilteredObjWidth:Number = 500, maxFilteredObjHeight:Number = 500) {
_hMtr = new Matrix();
_hPoint = new Point();
_hBmd = new BitmapData(maxFilteredObjWidth, maxFilteredObjHeight, true, 0);
_hBmdRect = new Rectangle(0, 0, maxFilteredObjWidth, maxFilteredObjHeight);
}
/**
* Calculates the boundary rectangle of an object relative to the given coordinate space.
*
* #param obj The object which bounds are to be determined.
*
* #param space The coordinate space relative to which the bounds should be represented.
* If you pass null or the object itself, then the bounds will be represented
* relative to the (untransformed) object.
*
* #param dst Destination rectangle to store the result in. If you pass null,
* new rectangle will be created and returned. Otherwise, the passed
* rectangle will be updated and returned.
*/
public function getRealBounds(obj:DisplayObject, space:DisplayObject = null, dst:Rectangle = null):Rectangle {
var tx:Number = (_hBmdRect.width - obj.width ) / 2,
ty:Number = (_hBmdRect.height - obj.height) / 2;
// get transformation matrix that translates the object to the center of the bitmap
_hMtr.identity();
_hMtr.translate(tx, ty);
// clear the bitmap so it will contain only pixels with zero alpha channel
_hBmd.fillRect(_hBmdRect, 0);
// draw the object; it will be drawn untransformed, except for translation
// caused by _hMtr matrix
_hBmd.draw(obj, _hMtr);
// get the area which encloses all pixels with nonzero alpha channel (i.e. our object)
var bnd:Rectangle = dst ? dst : new Rectangle(),
selfBnd:Rectangle = _hBmd.getColorBoundsRect(0xFF000000, 0x00000000, false);
// transform the area to eliminate the effect of _hMtr transformation; now we've obtained
// the bounds of the object in its own coord. system (self bounds)
selfBnd.offset(-tx, -ty);
if (space && space !== obj) { // the dst coord space is different from the object's one
// so we need to obtain transformation matrix from the object's coord space to the dst one
var mObjToSpace:Matrix;
if (space === obj.parent) {
// optimization
mObjToSpace = obj.transform.matrix;
} else if (space == obj.stage) {
// optimization
mObjToSpace = obj.transform.concatenatedMatrix;
} else {
// general case
var mStageToSpace:Matrix = space.transform.concatenatedMatrix; // space -> stage
mStageToSpace.invert(); // stage -> space
mObjToSpace = obj.transform.concatenatedMatrix; // obj -> stage
mObjToSpace.concat(mStageToSpace); // obj -> space
}
// now we transform all four vertices of the boundary rectangle to the target space
// and determine the bounds of this transformed shape
_xMin = Number.MAX_VALUE;
_xMax = -Number.MAX_VALUE;
_yMin = Number.MAX_VALUE;
_yMax = -Number.MAX_VALUE;
expandBounds(mObjToSpace.transformPoint(getPoint(selfBnd.x, selfBnd.y)));
expandBounds(mObjToSpace.transformPoint(getPoint(selfBnd.right, selfBnd.y)));
expandBounds(mObjToSpace.transformPoint(getPoint(selfBnd.x, selfBnd.bottom)));
expandBounds(mObjToSpace.transformPoint(getPoint(selfBnd.right, selfBnd.bottom)));
bnd.x = _xMin;
bnd.y = _yMin;
bnd.width = _xMax - _xMin;
bnd.height = _yMax - _yMin;
} else {
// the dst coord space is the object's one, so we simply return the self bounds
bnd.x = selfBnd.x;
bnd.y = selfBnd.y;
bnd.width = selfBnd.width;
bnd.height = selfBnd.height;
}
return bnd;
}
private function getPoint(x:Number, y:Number):Point {
_hPoint.x = x;
_hPoint.y = y;
return _hPoint;
}
private function expandBounds(p:Point):void {
if (p.x < _xMin) {
_xMin = p.x;
}
if (p.x > _xMax) {
_xMax = p.x;
}
if (p.y < _yMin) {
_yMin = p.y;
}
if (p.y > _yMax) {
_yMax = p.y;
}
}
}
}
I'm not sure this is possible using the normal methods of getBounds or getRect as these methods will just return the normal 100x100 square.
I have a few of suggestions for you.
Firstly you could apply the filters dynamically. That way you will have the numbers for the filters and you could do the maths programatically to work out what the actual size is.
You could add a second layer to the movieclip in the fla, which has the dimensions of your original square plus all the filters. Then set the alpha for this square to zero
Thirdly you could just have a png in the fla that contained the square plus, say, the glow within it.
Personally i'd go with the middle option as it would require the least amount of maintenance if the filters or the original square were to change.
Well, there is good news and bad news. The bad news is that there really isn't an effective way to do this "the right way". The good news is that there is an adequate way to approximate it.The general rule is that the size of the width is that it is approximately ( filter.blurX * 1.5 ) + sprite.width where "filter" is the Filter in the sprite.filters array. The same is true regarding blurY and height. The other general rule is that the minimum x becomes sprite.x - ( filter.blurX * 1.5 ) / 2;None of these numbers are "Adobe official", but the work within a reasonable enough margin of error to allow you to create a BitmapData based on that.

Resources