private Point3d computeWinCoord( Canvas3D canvas, RenderAtom ra, Point2d winCoord, Point3d objCoord, Transform3D localToImagePlate) { // Get local to Vworld transform RenderMolecule rm = ra.renderMolecule; if (rm == null) { // removeRenderAtom() may set ra.renderMolecule to null // in RenderBin before this renderer thread run. return null; } // MT safe issue: We can't reference ra.renderMolecule below since // RenderBin thread may set it to null anytime. Use rm instead. Transform3D lvw = rm.localToVworld[rm.localToVworldIndex[NodeRetained.LAST_LOCAL_TO_VWORLD]]; Point3d clipCoord3 = new Point3d(); clipCoord3.set(objCoord); Point4d clipCoord4 = new Point4d(); // Transform point from local coord. to clipping coord. lvw.transform(clipCoord3); canvas.vworldToEc.transform(clipCoord3); canvas.projTrans.transform(clipCoord3, clipCoord4); // clip check in Z if ((clipCoord4.w <= 0.0) || (clipCoord4.z > clipCoord4.w) || (-clipCoord4.z > clipCoord4.w)) { return null; } double invW = 1.0 / clipCoord4.w; clipCoord3.x = clipCoord4.x * invW; clipCoord3.y = clipCoord4.y * invW; clipCoord3.z = clipCoord4.z * invW; // Get Vworld to image plate Xform canvas.getLastVworldToImagePlate(localToImagePlate); // v' = vwip x lvw x v // where v' = transformed vertex, // lvw = local to Vworld Xform // vwip = Vworld to Image plate Xform // v = vertex // Compute composite local to image plate Xform localToImagePlate.mul(lvw); // Transform the Raster's position from object to world coordinates localToImagePlate.transform(objCoord); // Get the window coordinates of this point canvas.getPixelLocationFromImagePlate(objCoord, winCoord); return clipCoord3; }
// TODO -- Need to rethink. Might have to consider charTransform[] in returns pickInfo. @Override boolean intersect( PickShape pickShape, PickInfo pickInfo, int flags, Point3d iPnt, GeometryRetained geom, int geomIndex) { Transform3D tempT3D = new Transform3D(); GeometryArrayRetained geo = null; int sIndex = -1; PickShape newPS; double minDist = Double.MAX_VALUE; double distance = 0.0; Point3d closestIPnt = new Point3d(); for (int i = 0; i < numChars; i++) { geo = geometryList[i]; if (geo != null) { tempT3D.invert(charTransforms[i]); newPS = pickShape.transform(tempT3D); if (geo.intersect(newPS, pickInfo, flags, iPnt, geom, geomIndex)) { if (flags == 0) { return true; } distance = newPS.distance(iPnt); if (distance < minDist) { sIndex = i; minDist = distance; closestIPnt.set(iPnt); } } } } if (sIndex >= 0) { // We need to transform iPnt to the vworld to compute the actual distance. // In this method we'll transform iPnt by its char. offset. Shape3D will // do the localToVworld transform. iPnt.set(closestIPnt); charTransforms[sIndex].transform(iPnt); return true; } return false; }
@Override void execute( Canvas3D cv, RenderAtom ra, boolean isNonUniformScale, boolean updateAlpha, float alpha, int screen, boolean ignoreVertexColors) { // Compute the offset position of the raster // This has to be done at render time because we need access // to the Canvas3D info // Check if adjusted position needs to be computed Point3d adjPos = new Point3d(); // Position of the Raster after adjusting for dstOffset adjPos.set(position); Point2d winCoord = new Point2d(); // Position of Raster in window coordinates Transform3D localToImagePlate = new Transform3D(); // Local to Image plate transform Point3d clipCoord = computeWinCoord(cv, ra, winCoord, adjPos, localToImagePlate); // Test raster for out of bounds in Z. if (clipCoord == null) { return; } if (clipMode == Raster.CLIP_POSITION) { // Do trivial reject test on Raster position. if (!isRasterClipPositionInside(clipCoord)) { return; } } // Add the destination offset to the Raster position in window coordinates winCoord.x += xDstOffset; winCoord.y += yDstOffset; // System.err.println("Step 2 : adjPos " + adjPos + " winCoord " + winCoord); if ((type == Raster.RASTER_COLOR) || (type == Raster.RASTER_COLOR_DEPTH)) { float devCoordZ = (float) (clipCoord.z * 0.5 - 0.5); // Do textfill stuffs if (texture != null) { // setup Texture pipe. cv.updateTextureForRaster(texture); cv.textureFill(this, winCoord, devCoordZ, alpha); // Restore texture pipe. cv.restoreTextureBin(); } } if ((type == Raster.RASTER_DEPTH) || (type == Raster.RASTER_COLOR_DEPTH)) { Point2i srcOffset = new Point2i(xSrcOffset, ySrcOffset); if (clipMode == Raster.CLIP_IMAGE) { clipImage(cv, ra, winCoord, srcOffset); } computeObjCoord(cv, winCoord, adjPos, localToImagePlate); cv.executeRasterDepth( cv.ctx, (float) adjPos.x, (float) adjPos.y, (float) adjPos.z, srcOffset.x, srcOffset.y, width, height, depthComponent.width, depthComponent.height, depthComponent.type, ((DepthComponentIntRetained) depthComponent).depthData); } }
@Override synchronized void computeBoundingBox() { Point3d l = new Point3d(); Point3d u = new Point3d(); Vector3f location = new Vector3f(this.position); int i, k = 0, numTotal = 0; double width = 0, height = 0; Rectangle2D bounds; // Reset bounds data l.set(location); u.set(location); if (numChars != 0) { // Set loop counters based on path type if (path == Text3D.PATH_RIGHT || path == Text3D.PATH_UP) { k = 0; numTotal = numChars + 1; } else if (path == Text3D.PATH_LEFT || path == Text3D.PATH_DOWN) { k = 1; numTotal = numChars; // Reset bounds to bounding box if first character bounds = glyphVecs[0].getVisualBounds(); u.x += bounds.getWidth(); u.y += bounds.getHeight(); } for (i = 1; i < numTotal; i++, k++) { width = glyphVecs[k].getLogicalBounds().getWidth(); bounds = glyphVecs[k].getVisualBounds(); // 'switch' could be outside loop with little hacking, width += charSpacing; height = bounds.getHeight(); switch (this.path) { case Text3D.PATH_RIGHT: u.x += (width); if (u.y < (height + location.y)) { u.y = location.y + height; } break; case Text3D.PATH_LEFT: l.x -= (width); if (u.y < (height + location.y)) { u.y = location.y + height; } break; case Text3D.PATH_UP: u.y += height; if (u.x < (bounds.getWidth() + location.x)) { u.x = location.x + bounds.getWidth(); } break; case Text3D.PATH_DOWN: l.y -= height; if (u.x < (bounds.getWidth() + location.x)) { u.x = location.x + bounds.getWidth(); } break; } } // Handle string alignment. ALIGN_FIRST is handled by default if (alignment != Text3D.ALIGN_FIRST) { double cx = (u.x - l.x); double cy = (u.y - l.y); if (alignment == Text3D.ALIGN_CENTER) { cx *= .5; cy *= .5; } switch (path) { case Text3D.PATH_RIGHT: l.x -= cx; u.x -= cx; break; case Text3D.PATH_LEFT: l.x += cx; u.x += cx; break; case Text3D.PATH_UP: l.y -= cy; u.y -= cy; break; case Text3D.PATH_DOWN: l.y += cy; u.y += cy; break; } } } l.z = 0.0f; if ((font3D == null) || (font3D.fontExtrusion == null)) { u.z = l.z; } else { u.z = l.z + font3D.fontExtrusion.length; } }
/** * Update per character transform based on Text3D location, per character size and path. * * <p>WARNING: Caller of this method must make sure SceneGraph is live, else exceptions may be * thrown. */ final void updateTransformData() { int i, k = 0, numTotal = 0; double width = 0, height = 0; Vector3f location = new Vector3f(this.position); Rectangle2D bounds; // Reset bounds data lower.set(location); upper.set(location); charTransforms = new Transform3D[numChars]; for (i = 0; i < numChars; i++) { charTransforms[i] = new Transform3D(); } if (numChars != 0) { charTransforms[0].set(location); // Set loop counters based on path type if (path == Text3D.PATH_RIGHT || path == Text3D.PATH_UP) { k = 0; numTotal = numChars + 1; } else if (path == Text3D.PATH_LEFT || path == Text3D.PATH_DOWN) { k = 1; numTotal = numChars; // Reset bounds to bounding box if first character bounds = glyphVecs[0].getVisualBounds(); upper.x += bounds.getWidth(); upper.y += bounds.getHeight(); } for (i = 1; i < numTotal; i++, k++) { width = glyphVecs[k].getLogicalBounds().getWidth(); bounds = glyphVecs[k].getVisualBounds(); // 'switch' could be outside loop with little hacking, width += charSpacing; height = bounds.getHeight(); switch (this.path) { case Text3D.PATH_RIGHT: location.x += width; upper.x += (width); if (upper.y < (height + location.y)) { upper.y = location.y + height; } break; case Text3D.PATH_LEFT: location.x -= width; lower.x -= (width); if (upper.y < (height + location.y)) { upper.y = location.y + height; } break; case Text3D.PATH_UP: location.y += height; upper.y += height; if (upper.x < (bounds.getWidth() + location.x)) { upper.x = location.x + bounds.getWidth(); } break; case Text3D.PATH_DOWN: location.y -= height; lower.y -= height; if (upper.x < (bounds.getWidth() + location.x)) { upper.x = location.x + bounds.getWidth(); } break; } if (i < numChars) { charTransforms[i].set(location); } } // Handle string alignment. ALIGN_FIRST is handled by default if (alignment != Text3D.ALIGN_FIRST) { double cx = (upper.x - lower.x); double cy = (upper.y - lower.y); if (alignment == Text3D.ALIGN_CENTER) { cx *= .5; cy *= .5; } switch (path) { case Text3D.PATH_RIGHT: for (i = 0; i < numChars; i++) { charTransforms[i].mat[3] -= cx; } lower.x -= cx; upper.x -= cx; break; case Text3D.PATH_LEFT: for (i = 0; i < numChars; i++) { charTransforms[i].mat[3] += cx; } lower.x += cx; upper.x += cx; break; case Text3D.PATH_UP: for (i = 0; i < numChars; i++) { charTransforms[i].mat[7] -= cy; } lower.y -= cy; upper.y -= cy; break; case Text3D.PATH_DOWN: for (i = 0; i < numChars; i++) { charTransforms[i].mat[7] += cy; } lower.y += cy; upper.y += cy; break; } } } lower.z = 0.0f; if ((font3D == null) || (font3D.fontExtrusion == null)) { upper.z = lower.z; } else { upper.z = lower.z + font3D.fontExtrusion.length; } // update geoBounds getBoundingBox(geoBounds); }