/* This file is part of the source code for 3D-XplorMath-J, Version 1.0 (January 2008). * Copyright (c) 2008 The 3D-XplorMath Consortium (http://3d-xplormath.org). * This source code is released under a BSD License, which allows redistribution * in source and binary form, with or without modification, provided copyright * and license information are included, and with no warranty or guarantee of * any kind. For details, see http://3d-xplormath.org/j/source/BSDLicense.txt */ package vmm.core3D.render; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Font; import java.awt.Graphics2D; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.Stroke; import java.awt.geom.Ellipse2D; import java.awt.geom.GeneralPath; import java.awt.geom.Line2D; import java.awt.geom.Point2D; import java.awt.image.BufferedImage; import java.util.ArrayList; import vmm.core.Prefs; import vmm.core.Transform; import vmm.core.View; import vmm.core.render.ImageRenderer2D; import vmm.core3D.Grid3D; import vmm.core3D.StereoComposite; import vmm.core3D.Transform3D; import vmm.core3D.View3D; import vmm.core3D.Vector3D; import vmm.core3D.View3DLit; public class ImageRenderer3D extends ImageRenderer2D implements Renderer3D { protected View3D view3D; protected View3DLit view3DLit; // copy of view3D, or null if view3D is not an instance of view3DLit protected Transform3D transform3D; protected BufferedImage image2; // right-eye image for stereo viewing protected StereoComposite stereoComposite; protected Graphics2D leftEyeGraphics, rightEyeGraphics; protected Graphics2D leftEyeUntransformedGraphics, rightEyeUntransformedGraphics; protected Graphics2D saveGraphicsDuringStereo, saveUntransformedGraphicsDuringStereo; protected Rectangle leftEyeRect, rightEyeRect; protected int currentWidth, currentHeight; protected int viewStyle; private Vector3D viewDirection; private double clipZ; private double eyeSeparationMultiplier; public void startRender(View view, Transform transform, int width, int height) { if (! (view instanceof View3D) || ! (transform instanceof Transform3D) ) { view3D = null; // These will cause an error if any attempt is made to use 3D drawing methods. view3DLit = null; transform3D = null; viewStyle = View3D.MONOCULAR_VIEW; super.startRender(view,transform,width,height); return; } view3D = (View3D)view; view3DLit = (view instanceof View3DLit)? (View3DLit)view : null; this.transform = transform; transform3D = (Transform3D)transform; viewStyle = view3D.getViewStyle(); if (viewStyle != View3D.RED_GREEN_STEREO_VIEW && stereoComposite != null) { stereoComposite.releaseMemory(); stereoComposite = null; } int type = (view3DLit != null && view3DLit.getBlackAndWhite())? BufferedImage.TYPE_BYTE_GRAY : BufferedImage.TYPE_INT_RGB; switch (viewStyle) { case View3D.MONOCULAR_VIEW: image2 = null; if (image == null || image.getWidth() != width || image.getHeight() != height || image.getType() != type) { image = null; image = new BufferedImage(width,height,type); } break; case View3D.STEREOGRAPH_VIEW: case View3D.CROSS_EYE_STEREO_VIEW: Rectangle[] rects = view3D.getSterographViewRectangles(); leftEyeRect = rects[0]; rightEyeRect = rects[1]; int w = leftEyeRect.width; int h = leftEyeRect.height; if (image == null || image2 == null || image.getWidth() != w || image.getHeight() != h || image2.getWidth() != w || image2.getHeight() != h || image.getType() != type || image2.getType() != type) { image = image2 = null; image = new BufferedImage(w,h,type); image2 = new BufferedImage(w,h,type); } break; case View3D.RED_GREEN_STEREO_VIEW: image = image2 = null; if (stereoComposite == null) stereoComposite = new StereoComposite(); if (stereoComposite.getWidth() != width || stereoComposite.getHeight() != height) stereoComposite.setSize(width,height); image = stereoComposite.getLeftEyeImage(); image2 = stereoComposite.getRightEyeImage(); break; } setupRenderInfo(width,height,true); currentWidth = width; currentHeight = height; } private void setupRenderInfo(int width, int height, boolean clear) { viewDirection = transform3D.getViewDirection(); viewDirection.negate(); // since the view direction returned above points from the viewpoint towards the origen, and I want the reverse for clipping clipZ = transform3D.getFocalLength() - transform3D.getClipDistance(); eyeSeparationMultiplier = Prefs.getDouble("eyeSeparationMultiplier", 1); foregroundColor = viewStyle == View3D.RED_GREEN_STEREO_VIEW? Color.WHITE : view3D.getForeground(); backgroundColor = viewStyle == View3D.RED_GREEN_STEREO_VIEW? Color.BLACK: view3D.getBackground(); antialiased = view3D.getAntialiased(); currentColor = foregroundColor; if (viewStyle == View3D.MONOCULAR_VIEW) { currentGraphics = image.createGraphics(); currentGraphics.setBackground(backgroundColor); if (clear) currentGraphics.clearRect(0, 0, width, height); currentGraphics.setColor(currentColor); if (antialiased) currentGraphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON); transform.setUpDrawInfo(currentGraphics, 0, 0, width, height, view3D.getPreserveAspect(), view3D.getApplyGraphics2DTransform()); } else { leftEyeGraphics = image.createGraphics(); rightEyeGraphics = image2.createGraphics(); leftEyeGraphics.setColor(currentColor); leftEyeGraphics.setBackground(backgroundColor); if (clear) leftEyeGraphics.clearRect(0,0,image.getWidth(),image.getHeight()); rightEyeGraphics.setColor(currentColor); rightEyeGraphics.setBackground(backgroundColor); if (clear) rightEyeGraphics.clearRect(0,0,image.getWidth(),image.getHeight()); if (antialiased) { leftEyeGraphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON); rightEyeGraphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON); } transform3D.setUpDrawInfo(rightEyeGraphics,0,0,image2.getWidth(),image2.getHeight(), view3D.getPreserveAspect(),view3D.getApplyGraphics2DTransform()); rightEyeUntransformedGraphics = transform3D.getUntransformedGraphics(); transform3D.setUpDrawInfo(leftEyeGraphics,0,0,image.getWidth(),image.getHeight(), view3D.getPreserveAspect(),view3D.getApplyGraphics2DTransform()); leftEyeUntransformedGraphics = transform3D.getUntransformedGraphics(); currentGraphics = leftEyeGraphics; // should not be used -- there should be no 2D drawing in stereo mode ?? } } public boolean restartRender(View view, Transform transform, int width, int height) { if (! (view instanceof View3D) ) return super.restartRender(view,transform,width,height); if (((View3D)view).getViewStyle() != viewStyle || width != currentWidth || height != currentHeight) return false; view3D = (View3D)view; view3DLit = (view instanceof View3DLit)? (View3DLit)view : null; transform3D = (Transform3D)transform; this.transform = transform; setupRenderInfo(width,height,false); return true; } public void endRender() { if (view3D == null) { super.endRender(); return; } if (viewStyle == View3D.RED_GREEN_STEREO_VIEW) stereoComposite.compose(); transform3D.finishDrawing(); currentGraphics.dispose(); currentGraphics = null; if (viewStyle != View3D.MONOCULAR_VIEW) { rightEyeGraphics.dispose(); if (rightEyeUntransformedGraphics != rightEyeGraphics) rightEyeUntransformedGraphics.dispose(); leftEyeGraphics = rightEyeGraphics = leftEyeUntransformedGraphics = rightEyeUntransformedGraphics = null; } transform = null; view3D = null; view3DLit = null; transform3D = null; } public void dispose() { if (view3D == null) { super.dispose(); return; } image = image2 = null; if (stereoComposite != null) { stereoComposite.releaseMemory(); stereoComposite = null; } currentWidth = currentHeight = 0; } public void draw(Graphics2D g) { //System.out.println("Draw " + Math.random()); if (viewStyle == View3D.MONOCULAR_VIEW) g.drawImage(image,0,0,null); else if (viewStyle == View3D.RED_GREEN_STEREO_VIEW) g.drawImage(stereoComposite.getImage(),0,0,null); else { Color saveColor = g.getColor(); g.setColor(Color.WHITE); g.fillRect(0,0,currentWidth,currentHeight); g.drawImage(image,leftEyeRect.x,leftEyeRect.y,null); g.drawImage(image2,rightEyeRect.x,rightEyeRect.y,null); g.setColor(Color.GRAY); g.drawRect(leftEyeRect.x-1, leftEyeRect.y-1, leftEyeRect.width+1, leftEyeRect.width+1); g.drawRect(rightEyeRect.x-1, rightEyeRect.y-1, rightEyeRect.width+1, rightEyeRect.width+1); g.setColor(saveColor); } } public BufferedImage getImage(boolean alwaysCopy) { if (image == null) return null; if (!alwaysCopy) { if (viewStyle == View3D.MONOCULAR_VIEW) return image; if (viewStyle == View3D.RED_GREEN_STEREO_VIEW) return stereoComposite.getImage(); } int type; if (viewStyle == View3D.RED_GREEN_STEREO_VIEW) type = BufferedImage.TYPE_INT_RGB; else type = image.getType(); BufferedImage copy = new BufferedImage(currentWidth, currentHeight, type); Graphics2D g = copy.createGraphics(); draw(g); g.dispose(); return copy; } public void drawPixel(Vector3D v) { Point2D pt = new Point2D.Double(); if (clip(v)) return; int rgb = viewStyle == View3D.RED_GREEN_STEREO_VIEW? 0xFFFFFF : currentColor.getRGB(); if (viewStyle == View3D.MONOCULAR_VIEW) { transform3D.objectToXYWindowCoords(v,pt); transform3D.windowToViewport(pt); try { image.setRGB((int)pt.getX(),(int)pt.getY(),rgb); } catch (Exception e) { } } else { setUpForLeftEye(); transform3D.objectToXYWindowCoords(v,pt); transform3D.windowToViewport(pt); try { image.setRGB((int)pt.getX(),(int)pt.getY(),rgb); } catch (Exception e) { } setUpForRightEye(); transform3D.objectToXYWindowCoords(v,pt); transform3D.windowToViewport(pt); try { image2.setRGB((int)pt.getX(),(int)pt.getY(),rgb); } catch (Exception e) { } finishStereoView(); } } public void drawDot(Vector3D pt, double diameter) { double h = diameter*transform3D.getPixelWidth(); double w = diameter*transform3D.getPixelHeight(); Point2D pt2D = new Point2D.Double(); if (viewStyle == View3D.MONOCULAR_VIEW) { transform3D.objectToDrawingCoords(pt, pt2D); currentGraphics.fill(new Ellipse2D.Double(pt2D.getX()-h/2,pt2D.getY()-w/2,h,w)); } else { setUpForLeftEye(); transform3D.objectToDrawingCoords(pt, pt2D); currentGraphics.fill(new Ellipse2D.Double(pt2D.getX()-h/2,pt2D.getY()-w/2,h,w)); setUpForRightEye(); transform3D.objectToDrawingCoords(pt, pt2D); currentGraphics.fill(new Ellipse2D.Double(pt2D.getX()-h/2,pt2D.getY()-w/2,h,w)); finishStereoView(); } } public void drawPixels(Vector3D[] vlist) { Point2D pt = new Point2D.Double(); int rgb = viewStyle == View3D.RED_GREEN_STEREO_VIEW? 0xFFFFFF : currentColor.getRGB(); if (viewStyle == View3D.MONOCULAR_VIEW) { for (int i = 0; i < vlist.length; i++) if (vlist[i] != null && !clip(vlist[i])) { transform3D.objectToXYWindowCoords(vlist[i],pt); transform3D.windowToViewport(pt); try { image.setRGB((int)pt.getX(),(int)pt.getY(),rgb); } catch (Exception e) { } } } else { setUpForLeftEye(); for (int i = 0; i < vlist.length; i++) if (vlist[i] != null && !clip(vlist[i])) { transform3D.objectToXYWindowCoords(vlist[i],pt); transform3D.windowToViewport(pt); try { image.setRGB((int)pt.getX(),(int)pt.getY(),rgb); } catch (Exception e) { } } setUpForRightEye(); for (int i = 0; i < vlist.length; i++) if (vlist[i] != null && !clip(vlist[i])) { transform3D.objectToXYWindowCoords(vlist[i],pt); transform3D.windowToViewport(pt); try { image2.setRGB((int)pt.getX(),(int)pt.getY(),rgb); } catch (Exception e) { } } finishStereoView(); } } public void drawLine(Vector3D v1, Vector3D v2) { Point2D p1, p2; if (clip(v1) || clip(v2)) return; if (viewStyle == View3D.MONOCULAR_VIEW) { p1 = transform3D.objectToDrawingCoords(v1); p2 = transform3D.objectToDrawingCoords(v2); currentGraphics.draw(new Line2D.Float(p1,p2)); } else { setUpForLeftEye(); p1 = transform3D.objectToDrawingCoords(v1); p2 = transform3D.objectToDrawingCoords(v2); currentGraphics.draw(new Line2D.Float(p1,p2)); setUpForRightEye(); p1 = transform3D.objectToDrawingCoords(v1); p2 = transform3D.objectToDrawingCoords(v2); currentGraphics.draw(new Line2D.Float(p1,p2)); finishStereoView(); } } public void drawString(String str, Vector3D basepoint) { if (clip(basepoint)) return; if (viewStyle == View3D.MONOCULAR_VIEW) { Point2D p = transform3D.objectToXYWindowCoords(basepoint); super.drawString(str,p.getX(),p.getY()); // NOTE: Don't call drawString(str,Point2D) -- leads to infinite recursion } else { setUpForLeftEye(); Point2D p = transform3D.objectToXYWindowCoords(basepoint); super.drawString(str,p.getX(),p.getY()); setUpForRightEye(); p = transform3D.objectToXYWindowCoords(basepoint); super.drawString(str,p.getX(),p.getY()); finishStereoView(); } } public void drawCurve(Vector3D[] points, int startIndex, int endIndex) { if (points == null) return; if (startIndex >= points.length) startIndex = points.length - 1; if (startIndex < 0) startIndex = 0; if (endIndex >= points.length) endIndex = points.length - 1; if (endIndex < 0) endIndex = 0; if (endIndex <= startIndex) return; Point2D[] projectedPoints = new Point2D[endIndex - startIndex + 1]; if (viewStyle == View3D.MONOCULAR_VIEW) { for (int i = 0; i < projectedPoints.length; i++){ if (points[i - startIndex] == null || clip(points[i - startIndex])) projectedPoints[i] = null; else projectedPoints[i] = transform3D.objectToXYWindowCoords(points[i - startIndex]); } super.drawCurve(projectedPoints,0,projectedPoints.length-1); } else { setUpForLeftEye(); for (int i = 0; i < projectedPoints.length; i++){ if (points[i - startIndex] == null || clip(points[i - startIndex])) projectedPoints[i] = null; else projectedPoints[i] = transform3D.objectToXYWindowCoords(points[i - startIndex]); } super.drawCurve(projectedPoints,0,projectedPoints.length-1); setUpForRightEye(); for (int i = 0; i < projectedPoints.length; i++){ if (points[i - startIndex] == null || clip(points[i - startIndex])) projectedPoints[i] = null; else projectedPoints[i] = transform3D.objectToXYWindowCoords(points[i - startIndex]); } super.drawCurve(projectedPoints,0,projectedPoints.length-1); finishStereoView(); } } public void drawCollaredCurve(Vector3D[] points, int startIndex, int endIndex, boolean reversed) { if (points == null) return; if (!reversed && viewStyle != View3D.MONOCULAR_VIEW) { drawCurve(points,startIndex,endIndex); return; } if (startIndex >= points.length) startIndex = points.length - 1; if (startIndex < 0) startIndex = 0; if (endIndex >= points.length) endIndex = points.length - 1; if (endIndex < 0) endIndex = 0; if (endIndex <= startIndex) return; if (viewStyle == View3D.MONOCULAR_VIEW) drawCollaredCurveDirect(points,startIndex,endIndex,reversed); else { setUpForLeftEye(); drawCollaredCurveDirect(points,startIndex,endIndex,reversed); setUpForRightEye(); drawCollaredCurveDirect(points,startIndex,endIndex,reversed); finishStereoView(); } } public void drawWireframeSurface(Grid3D surfaceData) { if (viewStyle == View3D.MONOCULAR_VIEW) { surfaceData.applyTransform(transform3D, view3D); surfaceData.drawCurves(view3D, currentGraphics); } else { setUpForLeftEye(); surfaceData.applyTransform(transform3D, view3D); surfaceData.drawCurves(view3D, currentGraphics); setUpForRightEye(); surfaceData.applyTransform(transform3D, view3D); surfaceData.drawCurves(view3D, currentGraphics); finishStereoView(); } } public void drawSurface(Grid3D surfaceData, double startPercent, double endPercent) { if (view3DLit == null || ((View3DLit)view3D).getRenderingStyle() == View3DLit.WIREFRAME_RENDERING || (view3D.getFastDrawing() && !view3DLit.getDragAsSurface())) { drawWireframeSurface(surfaceData); } else if (((View3DLit)view3D).getRenderingStyle() == View3DLit.PATCH_RENDERING) { if (view3D.getViewStyle() == View3D.MONOCULAR_VIEW) { if (view3D.getAntialiased()) currentGraphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_OFF); surfaceData.applyTransform(transform3D, view3D); if (view3D.getFastDrawing()) surfaceData.drawMajorPatches(view3DLit, currentGraphics); else surfaceData.drawSubPatches(view3DLit, currentGraphics, startPercent, endPercent); if (view3D.getAntialiased()) currentGraphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON); } else { setUpForLeftEye(); if (view3D.getAntialiased()) currentGraphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_OFF); surfaceData.applyTransform(transform3D, view3D); if (view3D.getFastDrawing()) surfaceData.drawMajorPatches(view3DLit, currentGraphics); else surfaceData.drawSubPatches(view3DLit, currentGraphics, startPercent, endPercent); if (view3D.getAntialiased()) currentGraphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON); setUpForRightEye(); if (view3D.getAntialiased()) currentGraphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_OFF); surfaceData.applyTransform(transform3D, view3D); if (view3D.getFastDrawing()) surfaceData.drawMajorPatches(view3DLit, currentGraphics); else surfaceData.drawSubPatches(view3DLit, currentGraphics, startPercent, endPercent); if (view3D.getAntialiased()) currentGraphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON); finishStereoView(); } } } public void fillPolygon(Vector3D[] vertices, Color outlineColor) { if (view3D.getViewStyle() == View3D.MONOCULAR_VIEW) { Color saveColor = currentGraphics.getColor(); GeneralPath path = new GeneralPath(); Point2D pt = transform3D.objectToDrawingCoords(vertices[0]); path.moveTo( (float)pt.getX(), (float)pt.getY() ); for (int i = 1; i < vertices.length; i++) { pt = transform3D.objectToDrawingCoords(vertices[i]); path.lineTo( (float)pt.getX(), (float)pt.getY() ); } path.closePath(); currentGraphics.fill(path); if (outlineColor != null) currentGraphics.setColor(outlineColor); currentGraphics.draw(path); currentGraphics.setColor(saveColor); } else { setUpForLeftEye(); Color saveColor = currentGraphics.getColor(); GeneralPath path = new GeneralPath(); Point2D pt = transform3D.objectToDrawingCoords(vertices[0]); path.moveTo( (float)pt.getX(), (float)pt.getY() ); for (int i = 1; i < vertices.length; i++) { pt = transform3D.objectToDrawingCoords(vertices[i]); path.lineTo( (float)pt.getX(), (float)pt.getY() ); } path.closePath(); currentGraphics.fill(path); if (outlineColor != null) currentGraphics.setColor(outlineColor); currentGraphics.draw(path); currentGraphics.setColor(saveColor); setUpForRightEye(); saveColor = currentGraphics.getColor(); path.reset(); pt = transform3D.objectToDrawingCoords(vertices[0]); path.moveTo( (float)pt.getX(), (float)pt.getY() ); for (int i = 1; i < vertices.length; i++) { pt = transform3D.objectToDrawingCoords(vertices[i]); path.lineTo( (float)pt.getX(), (float)pt.getY() ); } path.closePath(); currentGraphics.fill(path); if (outlineColor != null) currentGraphics.setColor(outlineColor); currentGraphics.draw(path); currentGraphics.setColor(saveColor); finishStereoView(); } } // ------------------------- methods to support stereo rendering /** * To be used with the setUpForLeftEye and finishStereoView methods to support * 3D rendering. When rendering in one of the three stereo view styles, the image needs to be rendered * twice, once for the left eye and once for the right eye. The setUpForLeftEye methods * should be called first; this sets up the transform and current graphics context for rendering from * the viewpoint of the left eye into the left eye image. Next comes the code for drawing the object. * then setUpForRightEye, followed by a repeat of the code for drawing the object. * Finally, calling finshStereoView must be called to restore the original drawing * context. These three methods must always be used in this way. Note that most programmers will not * have to worry about this -- these methods will only be used when writing methods for rendering * new graphics primitives such as lines and surfaces in this class and its subclasses. */ protected void setUpForLeftEye() { double separationFactor; if (viewStyle == View3D.CROSS_EYE_STEREO_VIEW) separationFactor = 0.04; else if (viewStyle == View3D.STEREOGRAPH_VIEW) separationFactor = -0.03; else separationFactor = 0.03; separationFactor *= eyeSeparationMultiplier; transform3D.selectLeftEye(separationFactor); saveGraphicsDuringStereo = currentGraphics; saveUntransformedGraphicsDuringStereo = transform3D.getUntransformedGraphics(); Color color = saveGraphicsDuringStereo.getColor(); // copying color and stroke to left and right views, in case they were changed in currentGraphics leftEyeGraphics.setColor(color); rightEyeGraphics.setColor(color); Stroke stroke = saveGraphicsDuringStereo.getStroke(); leftEyeGraphics.setStroke(stroke); rightEyeGraphics.setStroke(stroke); Font font = saveGraphicsDuringStereo.getFont(); leftEyeGraphics.setFont(font); rightEyeGraphics.setFont(font); transform3D.useGraphics(leftEyeGraphics,leftEyeUntransformedGraphics); currentGraphics = leftEyeGraphics; } /** * To be used with {@link #setUpForLeftEye()} and finishStereoView to support * 3D rendering. */ protected void setUpForRightEye() { double separationFactor; if (viewStyle == View3D.CROSS_EYE_STEREO_VIEW) separationFactor = 0.04; else if (viewStyle == View3D.STEREOGRAPH_VIEW) separationFactor = -0.03; else separationFactor = 0.03; separationFactor *= eyeSeparationMultiplier; transform3D.selectRightEye(separationFactor); transform3D.useGraphics(rightEyeGraphics,rightEyeUntransformedGraphics); currentGraphics = rightEyeGraphics; } /** * To be used with {@link #setUpForLeftEye()} and {@link #setUpForRightEye()} to support * 3D rendering. */ protected void finishStereoView() { transform3D.selectNoEye(); transform3D.useGraphics(saveGraphicsDuringStereo,saveUntransformedGraphicsDuringStereo); currentGraphics = saveGraphicsDuringStereo; saveGraphicsDuringStereo = null; saveUntransformedGraphicsDuringStereo = null; } // ----------- Helper stuff for 3D rendering ----------------------------- private class CurveSegment extends Line2D.Double implements Comparable { Vector3D v1, v2; double midpoint_z; CurveSegment(Vector3D v1, Vector3D v2) { this.v1 = v1; this.v2 = v2; midpoint_z = (v1.z + v2.z)/2; Point2D p1 = new Point2D.Double(v1.x, v1.y); Point2D p2 = new Point2D.Double(v2.x, v2.y); if (!transform3D.appliedTransform2D()) { transform3D.windowToViewport(p1); transform3D.windowToViewport(p2); } setLine(p1,p2); } public int compareTo(CurveSegment c) { if (midpoint_z < c.midpoint_z) return -1; else if (midpoint_z > c.midpoint_z) return 1; else return 0; } } private void drawCollaredCurveDirect(Vector3D[] points, int startIndex, int endIndex, boolean reversed) { Vector3D p1 = points[startIndex]==null? null : transform3D.objectToViewCoords(points[startIndex]); ArrayList segments = new ArrayList(); for (int i = startIndex + 1; i <= endIndex; i++) { Vector3D p2 = points[i]==null || clip(points[i])? null : transform3D.objectToViewCoords(points[i]); if (p1 != null && p2 != null) segments.add(new CurveSegment(p1,p2)); p1 = p2; } java.util.Collections.sort(segments); Color saveColor = currentGraphics.getColor(); Stroke saveStroke = currentGraphics.getStroke(); Stroke wideStroke = new BasicStroke(transform3D.getDefaultStrokeSize() * 5, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER); Stroke normalStroke = new BasicStroke(transform3D.getDefaultStrokeSize(), BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER); for (int i = 0; i < segments.size(); i++) { currentGraphics.setStroke(wideStroke); currentGraphics.setColor(reversed ? saveColor : currentGraphics.getBackground()); currentGraphics.draw(segments.get(i)); currentGraphics.setStroke(normalStroke); currentGraphics.setColor(reversed ? currentGraphics.getBackground() : saveColor); currentGraphics.draw(segments.get(i)); } currentGraphics.setColor(saveColor); currentGraphics.setStroke(saveStroke); } /** * This method is used to test whether a point should be clipped. It is valid * only during a 3D rendering operation. It is needed in {@link Grid3D} and in * {@link vmm.polyhedron.IFS}. */ final public boolean clip(Vector3D objectPoint) { return objectPoint.dot(viewDirection) > clipZ; } }