/* This file contains proprietary software owned by Motorola Mobility, Inc.
No rights, expressed or implied, whatsoever to this software are provided by Motorola Mobility, Inc. hereunder.
(c) Copyright 2011 Motorola Mobility, Inc. All Rights Reserved.
*/ /////////////////////////////////////////////////////////////////////// // Class GLRectangle // GL representation of a rectangle. // Derived from class GLGeomObj /////////////////////////////////////////////////////////////////////// function GLRectangle() { // CONSTANTS this.N_TRIANGLES = 15; // initialize the inherited members this.inheritedFrom = GLGeomObj; this.inheritedFrom(); /////////////////////////////////////////////////////////////////////// // Instance variables /////////////////////////////////////////////////////////////////////// this._width = 2.0; this._height = 2.0; this._xOffset = 0; this._yOffset = 0; this._tlRadius = 0; this._trRadius = 0; this._blRadius = 0; this._brRadius = 0; this._strokeWidth = 0.25; this._strokeStyle = "Solid"; this.init = function(world, xOffset, yOffset, width, height, strokeSize, strokeColor, fillColor, tlRadius, trRadius, blRadius, brRadius, strokeMaterial, fillMaterial, strokeStyle) { this.m_world = world; if (arguments.length > 0) { this._width = width; this._height = height; this._xOffset = xOffset; this._yOffset = yOffset; this._strokeWidth = strokeSize; this._strokeColor = strokeColor; this._fillColor = fillColor; this.setTLRadius(tlRadius); this.setTRRadius(trRadius); this.setBLRadius(blRadius); this.setBRRadius(brRadius); this._strokeStyle = strokeStyle; } // the overall radius includes the fill and the stroke. separate the two based onthe stroke width // this._fillRad = this._radius - this._strokeWidth; // var err = 0.05; var err = 0; this._fillWidth = this._width - this._strokeWidth + err; this._fillHeight = this._height - this._strokeWidth + err; this._materialAmbient = [0.2, 0.2, 0.2, 1.0]; this._materialDiffuse = [0.4, 0.4, 0.4, 1.0]; this._materialSpecular = [0.4, 0.4, 0.4, 1.0]; if(strokeMaterial) this._strokeMaterial = strokeMaterial; else this._strokeMaterial = new FlatMaterial(); if(fillMaterial) this._fillMaterial = fillMaterial; else this._fillMaterial = new FlatMaterial(); } /////////////////////////////////////////////////////////////////////// // Property Accessors /////////////////////////////////////////////////////////////////////// this.getStrokeWidth = function() { return this._strokeWidth; } this.setStrokeWidth = function(w) { this._strokeWidth = w; } this.getStrokeMaterial = function() { return this._strokeMaterial; } this.setStrokeMaterial = function(m) { this._strokeMaterial = m; } this.getFillMaterial = function() { return this._fillMaterial; } this.setFillMaterial = function(m) { this._fillMaterial = m; } this.getStrokeColor = function() { return this._strokeColor; } //this.setStrokeColor = function(c) { this._strokeColor = c; } this.getFillColor = function() { return this._fillColor; } //this.setFillColor = function(c) { this._fillColor = c.slice(0); } this.getTLRadius = function() { return this._tlRadius; } this.setTLRadius = function(r) { this._tlRadius = Math.min(r, (this._height - this._strokeWidth)/2, (this._width - this._strokeWidth)/2); } this.getTRRadius = function() { return this._trRadius; } this.setTRRadius = function(r) { this._trRadius = Math.min(r, (this._height - this._strokeWidth)/2, (this._width - this._strokeWidth)/2); } this.getBLRadius = function() { return this._blRadius; } this.setBLRadius = function(r) { this._blRadius = Math.min(r, (this._height - this._strokeWidth)/2, (this._width - this._strokeWidth)/2); } this.getBRRadius = function() { return this._brRadius; } this.setBRRadius = function(r) { this._brRadius = Math.min(r, (this._height - this._strokeWidth)/2, (this._width - this._strokeWidth)/2); } this.getStrokeStyle = function() { return this._strokeStyle; } this.setStrokeStyle = function(s) { this._strokeStyle = s; } this.getWidth = function() { return this._width; } this.setWidth = function(w) { this._width = w; } this.getHeight = function() { return this._height; } this.setHeight = function(h) { this._height = h; } this.geomType = function() { return this.GEOM_TYPE_RECTANGLE; } /////////////////////////////////////////////////////////////////////// // Methods /////////////////////////////////////////////////////////////////////// this.export = function() { var rtnStr = "type: " + this.geomType() + "\n"; ///////////////////////////////////////////////////////////////////////// // // world, xOffset, yOffset, width, height, strokeSize, strokeColor, fillColor, // tlRadius, trRadius, blRadius, brRadius, strokeMaterial, fillMaterial, strokeStyle // ///////////////////////////////////////////////////////////////////////////// rtnStr += "xoff: " + this._xOffset + "\n"; rtnStr += "yoff: " + this._yOffset + "\n"; rtnStr += "width: " + this._width + "\n"; rtnStr += "height: " + this._height + "\n"; rtnStr += "strokeWidth: " + this._strokeWidth + "\n"; rtnStr += "strokeColor: " + String(this._strokeColor) + "\n"; rtnStr += "fillColor: " + String(this._fillColor) + "\n"; rtnStr += "tlRadius: " + this._tlRadius + "\n"; rtnStr += "trRadius: " + this._trRadius + "\n"; rtnStr += "blRadius: " + this._blRadius + "\n"; rtnStr += "brRadius: " + this._brRadius + "\n"; rtnStr += "innerRadius: " + this._innerRadius + "\n"; rtnStr += "strokeStyle: " + this._strokeStyle + "\n"; rtnStr += "strokeMat: "; if (this._strokeMaterial) rtnStr += this._strokeMaterial.getName(); else rtnStr += "flatMaterial"; rtnStr += "\n"; rtnStr += "fillMat: "; if (this._fillMaterial) rtnStr += this._fillMaterial.getName(); else rtnStr += "flatMaterial"; rtnStr += "\n"; return rtnStr; } this.import = function( importStr ) { this._xOffset = Number( this.getPropertyFromString( "xoff: ", importStr ) ); this._yOffset = Number( this.getPropertyFromString( "yoff: ", importStr ) ); this._width = Number( this.getPropertyFromString( "width: ", importStr ) ); this._height = Number( this.getPropertyFromString( "height: ", importStr ) ); this._strokeWidth = Number( this.getPropertyFromString( "strokeWidth: ", importStr ) ); this._innerRadius = Number( this.getPropertyFromString( "innerRadius: ", importStr ) ); this._strokeStyle = Number( this.getPropertyFromString( "strokeStyle: ", importStr ) ); var strokeMaterialName = this.getPropertyFromString( "strokeMat: ", importStr ); var fillMaterialName = this.getPropertyFromString( "fillMat: ", importStr ); this._strokeStyle = this.getPropertyFromString( "strokeStyle: ", importStr ); this._fillColor = eval( "[" + this.getPropertyFromString( "fillColor: ", importStr ) + "]" ); this._strokeColor = eval( "[" + this.getPropertyFromString( "strokeColor: ", importStr ) + "]" ); this._tlRadius = Number( this.getPropertyFromString( "tlRadius: ", importStr ) ); this._trRadius = Number( this.getPropertyFromString( "trRadius: ", importStr ) ); this._blRadius = Number( this.getPropertyFromString( "blRadius: ", importStr ) ); this._brRadius = Number( this.getPropertyFromString( "brRadius: ", importStr ) ); var strokeMat = MaterialsLibrary.getMaterial( strokeMaterialName ); if (!strokeMat) { console.log( "object material not found in library: " + strokeMaterialName ); strokeMat = new FlatMaterial(); } this._strokeMaterial = strokeMat; var fillMat = MaterialsLibrary.getMaterial( fillMaterialName ); if (!fillMat) { console.log( "object material not found in library: " + fillMaterialName ); fillMat = new FlatMaterial(); } this._fillMaterial = fillMat; } this.buildBuffers = function() { // get the world var world = this.getWorld(); if (!world) throw( "null world in buildBuffers" ); //console.log( "GLRectangle.buildBuffers " + world._worldCount ); if (!world._useWebGL) return; // make sure RDGE has the correct context g_Engine.setContext( world.getCanvas().uuid ); // create the gl buffer var gl = world.getGLContext(); var tlRadius = this._tlRadius; //top-left radius var trRadius = this._trRadius; var blRadius = this._blRadius; var brRadius = this._brRadius; // declare the arrays to hold the parts this._primArray = []; this._materialArray = []; this._materialTypeArray = []; this._materialNodeArray = []; // get the normalized device coordinates (NDC) for // all position and dimensions. var vpw = world.getViewportWidth(), vph = world.getViewportHeight(); var xNDC = 2*this._xOffset/vpw, yNDC = 2*this._yOffset/vph, xFillNDC = this._width/vpw, yFillNDC = this._height/vph, strokeSizeNDC = 2*this._strokeWidth/vpw, tlRadiusNDC = 2*tlRadius/vpw, yTLRadiusNDC = 2*tlRadius/vph, trRadiusNDC = 2*trRadius/vpw, yTRRadiusNDC = 2*trRadius/vph, blRadiusNDC = 2*blRadius/vpw, yBLRadiusNDC = 2*blRadius/vph, brRadiusNDC = 2*brRadius/vpw, yBRRadiusNDC = 2*brRadius/vph; var aspect = world.getAspect(); var zn = world.getZNear(), zf = world.getZFar(); var t = zn * Math.tan(world.getFOV() * Math.PI / 360.0), b = -t, r = aspect*t, l = -r; // calculate the object coordinates from their NDC coordinates var z = -world.getViewDistance(); // get the position of the origin var x = -z*(r-l)/(2.0*zn)*xNDC, y = -z*(t-b)/(2.0*zn)*yNDC; // get the x and y fill var xFill = -z*(r-l)/(2.0*zn)*xFillNDC, yFill = -z*(t-b)/(2.0*zn)*yFillNDC; // keep some variables giving the overall dimensions of the // rectangle. These values are used to calculate consistent // texture map coordinates across all pieces. this._rectWidth = xFill; this._rectHeight = yFill; // get the stroke size var strokeSize = -z*(r-l)/(2.0*zn)*strokeSizeNDC; // get the absolute corner radii tlRadius = -z*(r-l)/(2.0*zn)*tlRadiusNDC, trRadius = -z*(r-l)/(2.0*zn)*trRadiusNDC, blRadius = -z*(r-l)/(2.0*zn)*blRadiusNDC, brRadius = -z*(r-l)/(2.0*zn)*brRadiusNDC; // stroke var strokeMaterial = this.makeStrokeMaterial(); var strokePrim = this.createStroke([x,y], 2*xFill, 2*yFill, strokeSize, tlRadius, blRadius, brRadius, trRadius, strokeMaterial); this._primArray.push( strokePrim ); this._materialNodeArray.push( strokeMaterial.getMaterialNode() ); // fill tlRadius -= strokeSize; if (tlRadius < 0) tlRadius = 0.0; blRadius -= strokeSize; if (blRadius < 0) blRadius = 0.0; brRadius -= strokeSize; if (brRadius < 0) brRadius = 0.0; trRadius -= strokeSize; if (trRadius < 0) trRadius = 0.0; xFill -= strokeSize; yFill -= strokeSize; var fillMaterial = this.makeFillMaterial(); //console.log( "fillMaterial: " + fillMaterial.getName() ); var fillPrim = this.createFill([x,y], 2*xFill, 2*yFill, tlRadius, blRadius, brRadius, trRadius, fillMaterial); this._primArray.push( fillPrim ); this._materialNodeArray.push( fillMaterial.getMaterialNode() ); world.updateObject(this); } this.renderQuadraticBezier = function( bPts, ctx ) { if (!bPts) return; var nSegs = (bPts.length - 1)/2.0; if (nSegs <= 0) return; var index = 1; for (var i=0; i 0) ctx.quadraticCurveTo( inset, height-inset, inset+rad, height-inset ); // do the bottom of the rectangle pt = [width - inset, height - inset]; rad = brRad - inset; if (rad < 0) rad = 0; pt[0] -= rad; ctx.lineTo( pt[0], pt[1] ); // get the bottom right arc if (MathUtils.fpSign(rad) > 0) ctx.quadraticCurveTo( width-inset, height-inset, width-inset, height-inset-rad ); // get the right of the rectangle pt = [width - inset, inset]; rad = trRad - inset; if (rad < 0) rad = 0; pt[1] += rad; ctx.lineTo( pt[0], pt[1] ); // do the top right corner if (MathUtils.fpSign(rad) > 0) ctx.quadraticCurveTo( width-inset, inset, width-inset-rad, inset ); // do the top of the rectangle pt = [inset, inset] rad = tlRad - inset; if (rad < 0) rad = 0; pt[0] += rad; ctx.lineTo( pt[0], pt[1] ); // do the top left corner if (MathUtils.fpSign(rad) > 0) ctx.quadraticCurveTo( inset, inset, inset, inset+rad ); else ctx.lineTo( inset, 2*inset ); } } this.render = function() { // get the world var world = this.getWorld(); if (!world) throw( "null world in rectangle render" ); // get the context var ctx = world.get2DContext(); if (!ctx) return; // get some dimensions var lw = this._strokeWidth; var w = world.getViewportWidth(), h = world.getViewportHeight(); // set the fill ctx.beginPath(); ctx.fillStyle = "#990000"; if (this._fillColor) ctx.fillStyle = MathUtils.colorToHex( this._fillColor ); // set the stroke ctx.strokeStyle = "#0000ff"; if (this._strokeColor) ctx.strokeStyle = MathUtils.colorToHex( this._strokeColor ); ctx.lineWidth = lw; var inset = Math.ceil( 0.5*lw ) + 0.5; this.renderPath( inset, ctx ); ctx.fill(); ctx.stroke(); ctx.closePath(); } this.createStroke = function(ctr, width, height, strokeWidth, tlRad, blRad, brRad, trRad, material) { // create the geometry var prim = RectangleStroke.create( ctr, width, height, strokeWidth, tlRad, blRad, brRad, trRad, material) return prim; } this.createFill = function( ctr, width, height, tlRad, blRad, brRad, trRad, material) { // create the geometry // special the (common) case of no rounded corners var prim if ((tlRad <= 0) && (blRad <= 0) && (brRad <= 0) && (trRad <= 0)) prim = RectangleGeometry.create( ctr, width, height, material ); else prim = RectangleFill.create( ctr, width, height, tlRad, blRad, brRad, trRad, material); return prim; } this.collidesWithPoint = function( x, y ) { if(x < this._xOffset) return false; if(x > (this._xOffset + this._width)) return false; if(y < this._yOffset) return false; if(y > (this._yOffset + this._height)) return false; return true; } this.containsPoint = function( pt, dir ) { var world = this.getWorld(); if (!world) throw( "null world in containsPoint" ); // get a point on the plane of the circle // the point is in NDC, as is the input parameters var mat = this.getMatrix(); var plane = Vector.create([0,0,1,0]); plane = MathUtils.transformPlane( plane, mat ); var projPt = MathUtils.vecIntersectPlane ( pt, dir, plane ); // transform the projected point back to the XY plane //var invMat = mat.inverse(); var invMat = glmat4.inverse( mat, [] ); var planePt = MathUtils.transformPoint( projPt, invMat ); // get the normalized device coordinates (NDC) for // the position and radii. var vpw = world.getViewportWidth(), vph = world.getViewportHeight(); var xNDC = 2*this._xOffset/vpw, yNDC = 2*this._yOffset/vph, xRadNDC = this._width/vpw, yRadNDC = this._height/vph; var projMat = world.makePerspectiveMatrix(); var z = -world.getViewDistance(); var planePtNDC = planePt.slice(0); planePtNDC[2] = z; planePtNDC = MathUtils.transformHomogeneousPoint( planePtNDC, projMat ); planePtNDC = MathUtils.applyHomogeneousCoordinate( planePtNDC ); // get the center and dimensions of the rect in NDC var vpw = world.getViewportWidth(), vph = world.getViewportHeight(); var xNDC = 2*this._xOffset/vpw, yNDC = 2*this._yOffset/vph, hw = this._width/vpw, hh = this._height/vph; var x = planePtNDC[0], y = planePtNDC[1]; if (x < (xNDC - hw)) return false; if (x > (xNDC + hw)) return false; if (y < (yNDC - hh)) return false; if (y > (yNDC + hh)) return false; return true; } this.getNearVertex = function( pt, dir ) { var world = this.getWorld(); if (!world) throw( "null world in getNearPoint" ); // get a point on the plane of the circle // the point is in NDC, as is the input parameters var mat = this.getMatrix(); var plane = Vector.create([0,0,1,0]); plane = MathUtils.transformPlane( plane, mat ); var projPt = MathUtils.vecIntersectPlane ( pt, dir, plane ); // transform the projected point back to the XY plane //var invMat = mat.inverse(); var invMat = glmat4.inverse(mat, []); var planePt = MathUtils.transformPoint( projPt, invMat ); // get the normalized device coordinates (NDC) for // the position and radii. var vpw = world.getViewportWidth(), vph = world.getViewportHeight(); var xNDC = 2*this._xOffset/vpw, yNDC = 2*this._yOffset/vph, hwNDC = this._width/vpw, hhNDC = this._height/vph; var projMat = world.makePerspectiveMatrix(); var z = -world.getViewDistance(); var planePtNDC = planePt.slice(0); planePtNDC[2] = z; planePtNDC = MathUtils.transformHomogeneousPoint( planePtNDC, projMat ); planePtNDC = MathUtils.applyHomogeneousCoordinate( planePtNDC ); // get the near point in NDC var x = planePtNDC[0], y = planePtNDC[1]; var xMin = xNDC - hwNDC, xMax = xNDC + hwNDC, yMin = yNDC - hhNDC, yMax = yNDC + hhNDC; // compare the point against the 4 corners var pt, dist; pt = Vector.create( [xMin, yMin, 0] ); dist = VecUtils.vecDist(2, pt, planePtNDC); var minPt = pt, minDist = dist; pt = Vector.create( [xMin, yMax, 0] ); dist = VecUtils.vecDist(2, pt, planePtNDC); if (dist < minDist) { minDist = dist; minPt = pt; } pt = Vector.create( [xMax, yMax, 0] ); dist = VecUtils.vecDist(2, pt, planePtNDC); if (dist < minDist) { minDist = dist; minPt = pt; } pt = Vector.create( [xMax, yMin, 0] ); dist = VecUtils.vecDist(2, pt, planePtNDC); if (dist < minDist) { minDist = dist; minPt = pt; } // convert to GL coordinates x = minPt[0]; y = minPt[1]; var aspect = world.getAspect(); var zn = world.getZNear(), zf = world.getZFar(); var t = zn * Math.tan(world.getFOV() * Math.PI / 360.0), b = -t, r = aspect*t, l = -r; var objPt = Vector.create([0,0,0]); objPt[0] = -z*(r-l)/(2.0*zn)*x; objPt[1] = -z*(t-b)/(2.0*zn)*y; // re-apply the transform objPt = MathUtils.transformPoint( objPt, mat ); return objPt; } this.getNearPoint = function( pt, dir ) { var world = this.getWorld(); if (!world) throw( "null world in getNearPoint" ); // get a point on the plane of the circle // the point is in NDC, as is the input parameters var mat = this.getMatrix(); var plane = Vector.create([0,0,1,0]); plane = MathUtils.transformPlane( plane, mat ); var projPt = MathUtils.vecIntersectPlane ( pt, dir, plane ); // transform the projected point back to the XY plane //var invMat = mat.inverse(); var invMat = glmat4.inverse(mat, []); var planePt = MathUtils.transformPoint( projPt, invMat ); // get the normalized device coordinates (NDC) for // the position and radii. var vpw = world.getViewportWidth(), vph = world.getViewportHeight(); var xNDC = 2*this._xOffset/vpw, yNDC = 2*this._yOffset/vph, hwNDC = this._width/vpw, hhNDC = this._height/vph; var projMat = world.makePerspectiveMatrix(); var z = -world.getViewDistance(); var planePtNDC = planePt.slice(0); planePtNDC[2] = z; planePtNDC = MathUtils.transformHomogeneousPoint( planePtNDC, projMat ); planePtNDC = MathUtils.applyHomogeneousCoordinate( planePtNDC ); // get the near point in NDC var x = planePtNDC[0], y = planePtNDC[1]; var xMin = xNDC - hwNDC, xMax = xNDC + hwNDC, yMin = yNDC - hhNDC, yMax = yNDC + hhNDC; // compare the point against the near point on the 4 sides var pt, dist; pt = Vector.create( [xMin, y, 0] ); if (pt[1] < yMin) pt[1] = yMin; else if (pt[1] > yMax) pt[1] = yMax; dist = VecUtils.vecDist(2, pt, planePtNDC); var minPt = pt, minDist = dist; pt = Vector.create( [x, yMax, 0] ); if (pt[0] < xMin) pt[0] = xMin; else if (pt[0] > xMax) pt[0] = xMax; dist = VecUtils.vecDist(2, pt, planePtNDC); if (dist < minDist) { minDist = dist; minPt = pt; } pt = Vector.create( [xMax, y, 0] ); if (pt[1] < yMin) pt[1] = yMin; else if (pt[1] > yMax) pt[1] = yMax; dist = VecUtils.vecDist(2, pt, planePtNDC); if (dist < minDist) { minDist = dist; minPt = pt; } pt = Vector.create( [x, yMin, 0] ); if (pt[0] < xMin) pt[0] = xMin; else if (pt[0] > xMax) pt[0] = xMax; dist = VecUtils.vecDist(2, pt, planePtNDC); if (dist < minDist) { minDist = dist; minPt = pt; } // convert to GL coordinates x = minPt[0]; y = minPt[1]; var aspect = world.getAspect(); var zn = world.getZNear(), zf = world.getZFar(); var t = zn * Math.tan(world.getFOV() * Math.PI / 360.0), b = -t, r = aspect*t, l = -r; var objPt = Vector.create([0,0,0]); objPt[0] = -z*(r-l)/(2.0*zn)*x; objPt[1] = -z*(t-b)/(2.0*zn)*y; // re-apply the transform objPt = MathUtils.transformPoint( objPt, mat ); return objPt; } this.recalcTexMapCoords = function( vrts, uvs ) { var n = vrts.length/3; var ivrt = 0, iuv = 0; for (var i=0; i minDimen) tlRad = minDimen; if (blRad > minDimen) blRad = minDimen; if (brRad > minDimen) brRad = minDimen; if (trRad > minDimen) trRad = minDimen; // define some local variables this.vertices = []; this.normals = []; this.uvs = []; this.indices = []; // the center of the rectangle is the first vertex RectangleFill.pushVertex( x, y, z ); // traverse the perimiter of the rectangle // push the starting point RectangleFill.pushVertex( x-hw, y+hh-tlRad, z); // do the left side var ctr; if (blRad <= 0) RectangleFill.pushVertex( x-hw, y-hh, z); else { ctr = [x - hw + blRad, y - hh + blRad, z]; RectangleFill.getRoundedCorner( ctr, [x-hw, y-hh+blRad, z], this.vertices ); } // do the bottom if (brRad <= 0) RectangleFill.pushVertex( x+hw, y-hh, z); else { ctr = [x + hw - brRad, y - hh + brRad, z]; RectangleFill.getRoundedCorner( ctr, [x+hw-brRad, y-hh, z], this.vertices ); } // do the right if (trRad <= 0) RectangleFill.pushVertex( x+hw, y+hh, z); else { ctr = [x + hw - trRad, y + hh - trRad, z]; RectangleFill.getRoundedCorner( ctr, [x+hw, y+hh-trRad, z], this.vertices ); } // do the top if (tlRad <= 0) RectangleFill.pushVertex( x-hw, y+hh, z); else { ctr = [x - hw + tlRad, y + hh - tlRad, z]; RectangleFill.getRoundedCorner( ctr, [x-hw+tlRad, y+hh, z], this.vertices ); } // get the normals and uvs var vrt, uv; var xMin = x - hw, yMin = y - hh; var n = [0, 0, 1]; var nVertices = this.vertices.length / 3; for (var i=0; i minDimen) tlRad = minDimen; if (blRad > minDimen) blRad = minDimen; if (brRad > minDimen) brRad = minDimen; if (trRad > minDimen) trRad = minDimen; // define some local variables this.vertices = []; this.normals = []; this.uvs = []; this.indices = []; // get the starting points if (tlRad == 0) { RectangleStroke.pushVertex( x-hw+sw, y+hh-sw, z); RectangleStroke.pushVertex( x-hw, y+hh, z); } else { if (tlRad > sw) { RectangleStroke.pushVertex( x-hw+sw, y+hh-tlRad, z); RectangleStroke.pushVertex( x-hw, y+hh-tlRad, z); } else { RectangleStroke.pushVertex( x-hw+tlRad, y+hh-tlRad, z); RectangleStroke.pushVertex( x-hw, y+hh-tlRad, z); RectangleStroke.pushVertex( x-hw+sw, y+hh-sw, z); RectangleStroke.pushVertex( x-hw, y+hh-sw, z); } } // get the left side if (blRad == 0) { RectangleStroke.pushVertex( x-hw+sw, y-hh+sw, z); RectangleStroke.pushVertex( x-hw, y-hh, z); } else { if (blRad >= sw) { RectangleStroke.pushVertex( x-hw+sw, y-hh+blRad, z); RectangleStroke.pushVertex( x-hw, y-hh+blRad, z); var ctr = [x-hw+blRad, y-hh+blRad, z], insidePt = [x-hw+sw, y-hh+blRad, z], outsidePt = [x-hw, y-hh+blRad, z]; RectangleStroke.getRoundedCorner( ctr, insidePt, outsidePt, this.vertices ); } else { RectangleStroke.pushVertex( x-hw+sw, y-hh+sw, z); RectangleStroke.pushVertex( x-hw, y-hh+blRad, z); var ctr = [x-hw+blRad, y-hh+blRad, z], insidePt = [x-hw+blRad, y-hh+blRad, z], outsidePt = [x-hw, y-hh+blRad, z]; RectangleStroke.getRoundedCorner( ctr, insidePt, outsidePt, this.vertices ); RectangleStroke.pushVertex( x-hw+sw, y-hh+sw, z); RectangleStroke.pushVertex( x-hw+sw, y-hh, z); } } // get the bottom if (brRad == 0) { RectangleStroke.pushVertex( x+hw-sw, y-hh+sw, z); RectangleStroke.pushVertex( x+hw, y-hh, z); } else { RectangleStroke.pushVertex( x+hw-brRad, y-hh+sw, z); RectangleStroke.pushVertex( x+hw-brRad, y-hh, z); if (brRad >= sw) { var ctr = [x+hw-brRad, y-hh+brRad, z], insidePt = [x+hw-brRad, y-hh+sw, z], outsidePt = [x+hw-brRad, y-hh, z]; RectangleStroke.getRoundedCorner( ctr, insidePt, outsidePt, this.vertices ); } else { RectangleStroke.pushVertex( x+hw-sw, y-hh+sw, z); RectangleStroke.pushVertex( x+hw-brRad, y-hh, z); var ctr = [x+hw-brRad, y-hh+brRad, z], insidePt = [x+hw-brRad, y-hh+brRad, z], outsidePt = [x+hw-brRad, y-hh, z]; RectangleStroke.getRoundedCorner( ctr, insidePt, outsidePt, this.vertices ); RectangleStroke.pushVertex( x+hw-sw, y-hh+sw, z); RectangleStroke.pushVertex( x+hw, y-hh+sw, z); } } // get the right if (trRad == 0) { RectangleStroke.pushVertex( x+hw-sw, y+hh-sw, z); RectangleStroke.pushVertex( x+hw, y+hh, z); } else { if (trRad >= sw) { RectangleStroke.pushVertex( x+hw-sw, y+hh-trRad, z); RectangleStroke.pushVertex( x+hw, y+hh-trRad, z); var ctr = [x+hw-trRad, y+hh-trRad, z], insidePt = [x+hw-sw, y+hh-trRad, z], outsidePt = [x+hw, y+hh-trRad, z]; RectangleStroke.getRoundedCorner( ctr, insidePt, outsidePt, this.vertices ); } else { RectangleStroke.pushVertex( x+hw-sw, y+hh-sw, z); RectangleStroke.pushVertex( x+hw, y+hh-trRad, z); var ctr = [x+hw-trRad, y+hh-trRad, z], insidePt = [x+hw-trRad, y+hh-trRad, z], outsidePt = [x+hw, y+hh-trRad, z]; RectangleStroke.getRoundedCorner( ctr, insidePt, outsidePt, this.vertices ); RectangleStroke.pushVertex( x+hw-sw, y+hh-sw, z); RectangleStroke.pushVertex( x+hw-sw, y+hh, z); } } // get the top if (tlRad == 0) { RectangleStroke.pushVertex( x-hw+sw, y+hh-sw, z); RectangleStroke.pushVertex( x-hw, y+hh, z); } else { if (tlRad >= sw) { RectangleStroke.pushVertex( x-hw+tlRad, y+hh-sw, z); RectangleStroke.pushVertex( x-hw+tlRad, y+hh, z); var ctr = [x-hw+tlRad, y+hh-tlRad, z], insidePt = [x-hw+tlRad, y+hh-sw, z], outsidePt = [x-hw+tlRad, y+hh, z]; RectangleStroke.getRoundedCorner( ctr, insidePt, outsidePt, this.vertices ); } else { RectangleStroke.pushVertex( x-hw+sw, y+hh-sw, z); RectangleStroke.pushVertex( x-hw+tlRad, y+hh, z); var ctr = [x-hw+tlRad, y+hh-tlRad, z], insidePt = [x-hw+tlRad, y+hh-tlRad, z], outsidePt = [x-hw+tlRad, y+hh, z]; RectangleStroke.getRoundedCorner( ctr, insidePt, outsidePt, this.vertices ); } } // get the normals and uvs var vrt, uv; var xMin = x - hw, yMin = y - hh; var n = [0, 0, 1]; var nVertices = this.vertices.length / 3; for (var i=0; i uMax) uMax = u1; if (v1 > vMax) vMax = v1; if (u2 > uMax) uMax = u2; if (v2 > vMax) vMax = v2; // if the parameter range of the triangle is outside the // desired parameter range, advance to the next polygon and continue if ((uMin > pUMax) || (uMax < pUMin) || (vMin > pVMax) || (vMax < pVMin)) { // go to the next triangle iTriangle++; index += 3; } else { // check thesize of the triangle in uv space. If small enough, advance // to the next triangle. If not small enough, split the triangle into 3; var du = uMax - uMin, dv = vMax - vMin; if ((du < tolerance) && (dv < tolerance)) { iTriangle++; index += 3; } else // split the triangle into 4 parts { //calculate the position of the new vertex var iPt0 = 3 * i0, iPt1 = 3 * i1, iPt2 = 3 * i2; var x0 = verts[iPt0], y0 = verts[iPt0+1], z0 = verts[iPt0+2], x1 = verts[iPt1], y1 = verts[iPt1+1], z1 = verts[iPt1+2], x2 = verts[iPt2], y2 = verts[iPt2+1], z2 = verts[iPt2+2]; // calculate the midpoints of the edges var xA = (x0 + x1)/2.0, yA = (y0 + y1)/2.0, zA = (z0 + z1)/2.0, xB = (x1 + x2)/2.0, yB = (y1 + y2)/2.0, zB = (z1 + z2)/2.0, xC = (x2 + x0)/2.0, yC = (y2 + y0)/2.0, zC = (z2 + z0)/2.0; // calculate the uv values of the new coordinates var uA = (u0 + u1)/2.0, vA = (v0 + v1)/2.0, uB = (u1 + u2)/2.0, vB = (v1 + v2)/2.0, uC = (u2 + u0)/2.0, vC = (v2 + v0)/2.0; // calculate the normals for the new points var nx0 = norms[iPt0], ny0 = norms[iPt0+1], nz0 = norms[iPt0+2], nx1 = norms[iPt1], ny1 = norms[iPt1+1], nz1 = norms[iPt1+2], nx2 = norms[iPt2], ny2 = norms[iPt2+1], nz2 = norms[iPt2+2]; var nxA = (nx0 + nx1), nyA = (ny0 + ny1), nzA = (nz0 + nz1); var nrmA = VecUtils.vecNormalize(3, [nxA, nyA, nzA], 1.0 ), nxB = (nx1 + nx2), nyB = (ny1 + ny2), nzB = (nz1 + nz2); var nrmB = VecUtils.vecNormalize(3, [nxB, nyB, nzB], 1.0 ), nxC = (nx2 + nx0), nyC = (ny2 + ny0), nzC = (nz2 + nz0); var nrmC = VecUtils.vecNormalize(3, [nxC, nyC, nzC], 1.0 ); // push everything verts.push(xA); verts.push(yA); verts.push(zA); verts.push(xB); verts.push(yB); verts.push(zB); verts.push(xC); verts.push(yC); verts.push(zC); uvs.push(uA), uvs.push(vA); uvs.push(uB), uvs.push(vB); uvs.push(uC), uvs.push(vC); norms.push(nrmA[0]); norms.push(nrmA[1]); norms.push(nrmA[2]); norms.push(nrmB[0]); norms.push(nrmB[1]); norms.push(nrmB[2]); norms.push(nrmC[0]); norms.push(nrmC[1]); norms.push(nrmC[2]); // split the current triangle into 4 indices[index+1] = nVertices; indices[index+2] = nVertices+2; indices.push(nVertices); indices.push(i1); indices.push(nVertices+1); nTriangles++; indices.push(nVertices+1); indices.push(i2); indices.push(nVertices+2); nTriangles++; indices.push(nVertices); indices.push(nVertices+1); indices.push(nVertices+2); nTriangles++; nVertices += 3; // by not advancing 'index', we examine the first of the 3 triangles generated above } } } return nVertices; }