From 288b94bce820eba599e29732cbc57e61a43621c2 Mon Sep 17 00:00:00 2001 From: Bill Kuker Date: Tue, 14 Apr 2009 19:05:57 +0000 Subject: [PATCH] Separated out birning shape so that I might use it for the rotated grain --- .../rocketry/motorsim/grain/BurningShape.java | 111 ++++++++ .../motorsim/grain/ExtrudedShapeGrain.java | 241 +----------------- .../rocketry/motorsim/grain/ShapeUtil.java | 153 +++++++++++ 3 files changed, 277 insertions(+), 228 deletions(-) create mode 100644 src/com/billkuker/rocketry/motorsim/grain/BurningShape.java create mode 100644 src/com/billkuker/rocketry/motorsim/grain/ShapeUtil.java diff --git a/src/com/billkuker/rocketry/motorsim/grain/BurningShape.java b/src/com/billkuker/rocketry/motorsim/grain/BurningShape.java new file mode 100644 index 0000000..31d9b0f --- /dev/null +++ b/src/com/billkuker/rocketry/motorsim/grain/BurningShape.java @@ -0,0 +1,111 @@ +package com.billkuker.rocketry.motorsim.grain; + +import java.awt.Shape; +import java.awt.geom.Ellipse2D; +import java.awt.geom.Rectangle2D; +import java.util.HashSet; +import java.util.Set; + +import javax.measure.quantity.Length; +import javax.measure.unit.SI; + +import org.jscience.physics.amount.Amount; + +public class BurningShape { + Set plus = new HashSet(); + Set minus = new HashSet(); + Set inhibited = new HashSet(); + + public void add(Shape s){ + plus.add(s); + } + + public void subtract(Shape s){ + minus.add(s); + } + + public void inhibit(Shape s){ + inhibited.add(s); + } + + public java.awt.geom.Area getShape(Amount regression) { + java.awt.geom.Area res = getPlus(regression); + res.subtract(getMinus(regression)); + return res; + } + + + public java.awt.geom.Area getPlus(Amount regression) { + java.awt.geom.Area a = new java.awt.geom.Area(); + for (Shape s : plus) + a.add(new java.awt.geom.Area(regress(s, regression + .doubleValue(SI.MILLIMETER), true))); + return a; + } + + public java.awt.geom.Area getMinus(Amount regression) { + java.awt.geom.Area a = new java.awt.geom.Area(); + for (Shape s : minus) + a.add(new java.awt.geom.Area(regress(s, regression + .doubleValue(SI.MILLIMETER), false))); + return a; + } + + private Shape regress(Shape s, double mm, boolean plus) { + if (inhibited.contains(s)) + return s; + if (s instanceof Ellipse2D) { + Ellipse2D e = (Ellipse2D) s; + + double d = plus ? -2 * mm : 2 * mm; + + double w = e.getWidth() + d; + double h = e.getHeight() + d; + double x = e.getX() - d / 2; + double y = e.getY() - d / 2; + + return new Ellipse2D.Double(x, y, w, h); + } else if (s instanceof Rectangle2D) { + Rectangle2D r = (Rectangle2D) s; + + if ( plus ){ + double d = -2 * mm; + double w = r.getWidth() + d; + double h = r.getHeight() + d; + double x = r.getX() - d / 2; + double y = r.getY() - d / 2; + return new Rectangle2D.Double(x, y, w, h); + } else { + //A rectangular hole gets rounded corners as it grows + java.awt.geom.Area a = new java.awt.geom.Area(); + double d = 2 * mm; + + //Make it wider + double w = r.getWidth() + d; + double h = r.getHeight(); + double x = r.getX() - d / 2; + double y = r.getY(); + a.add( new java.awt.geom.Area(new Rectangle2D.Double(x, y, w, h))); + + //Make it taller + w = r.getWidth(); + h = r.getHeight() + d; + x = r.getX(); + y = r.getY() - d / 2; + a.add( new java.awt.geom.Area(new Rectangle2D.Double(x, y, w, h))); + + //Add rounded corners + a.add( new java.awt.geom.Area(new Ellipse2D.Double(r.getX()-mm, r.getY()-mm, mm*2, mm*2))); + a.add( new java.awt.geom.Area(new Ellipse2D.Double(r.getX()+r.getWidth()-mm, r.getY()-mm, mm*2, mm*2))); + a.add( new java.awt.geom.Area(new Ellipse2D.Double(r.getX()+r.getWidth()-mm, r.getY()+r.getHeight()-mm, mm*2, mm*2))); + a.add( new java.awt.geom.Area(new Ellipse2D.Double(r.getX()-mm, r.getY()+r.getHeight()-mm, mm*2, mm*2))); + + return a; + } + + } + return null; + } + + +} diff --git a/src/com/billkuker/rocketry/motorsim/grain/ExtrudedShapeGrain.java b/src/com/billkuker/rocketry/motorsim/grain/ExtrudedShapeGrain.java index e9557eb..1a8851e 100644 --- a/src/com/billkuker/rocketry/motorsim/grain/ExtrudedShapeGrain.java +++ b/src/com/billkuker/rocketry/motorsim/grain/ExtrudedShapeGrain.java @@ -25,11 +25,7 @@ import com.billkuker.rocketry.motorsim.visual.GrainPanel; public class ExtrudedShapeGrain extends MotorPart implements Grain { - Set plus = new HashSet(); - - Set minus = new HashSet(); - - Set inhibited = new HashSet(); + BurningShape xsection = new BurningShape(); Amount length = Amount.valueOf(25, SI.MILLIMETER); @@ -48,9 +44,9 @@ public class ExtrudedShapeGrain extends MotorPart implements Grain { /* Big c-slot */ Shape outside = new Ellipse2D.Double(0, 0, 30, 30); - plus.add(outside); - inhibited.add(outside); - minus.add(new Rectangle2D.Double(13, 13, 4, 30)); + xsection.add(outside); + xsection.inhibit(outside); + xsection.subtract(new Rectangle2D.Double(13, 13, 4, 30)); //minus.add(new Ellipse2D.Double(12, 12, 6, 6)); length = Amount.valueOf(70, SI.MILLIMETER); /**/ @@ -90,7 +86,7 @@ public class ExtrudedShapeGrain extends MotorPart implements Grain { Amount xSection = crossSectionArea(regression); - return perimeter(burn).divide(2).times(rLen).plus( + return ShapeUtil.perimeter(burn).divide(2).times(rLen).plus( xSection.times(2)).to(Area.UNIT); } @@ -116,19 +112,19 @@ public class ExtrudedShapeGrain extends MotorPart implements Grain { private Amount crossSectionArea(Amount regression) { //Get the PLUS shape and sum its area - java.awt.geom.Area plus = getPlus(regression); + java.awt.geom.Area plus = xsection.getPlus(regression); Amount plusArea = Amount.valueOf(0, SI.SQUARE_METRE); - for (java.awt.geom.Area a : separate(plus)) { - plusArea = plusArea.plus(area(a)); + for (java.awt.geom.Area a : ShapeUtil.separate(plus)) { + plusArea = plusArea.plus(ShapeUtil.area(a)); } //Get the MINUS shape, intersect it with PLUS to get just the parts //that are removed, sum it's area - java.awt.geom.Area minus = getMinus(regression); + java.awt.geom.Area minus = xsection.getMinus(regression); minus.intersect(plus); Amount minusArea = Amount.valueOf(0, SI.SQUARE_METRE); - for (java.awt.geom.Area a : separate(minus)) { - minusArea = minusArea.plus(area(a)); + for (java.awt.geom.Area a : ShapeUtil.separate(minus)) { + minusArea = minusArea.plus(ShapeUtil.area(a)); } //Subtract PLUS from MINUS and return @@ -142,34 +138,6 @@ public class ExtrudedShapeGrain extends MotorPart implements Grain { return webThickness; } - private Amount perimeter(java.awt.geom.Area a) { - //TODO: I think I need to handle seg_close!! - PathIterator i = a.getPathIterator(new AffineTransform(), .001); - double x = 0, y = 0; - double len = 0; - while (!i.isDone()) { - double coords[] = new double[6]; - int type = i.currentSegment(coords); - if (type == PathIterator.SEG_LINETO) { - // System.out.println("Line"); - double nx = coords[0]; - double ny = coords[1]; - // System.out.println(x+","+y+ " to " + nx+"," + ny); - len += Math.sqrt(Math.pow(x - nx, 2) + Math.pow(y - ny, 2)); - x = nx; - y = ny; - } else if (type == PathIterator.SEG_MOVETO) { - // System.out.println("Move"); - x = coords[0]; - y = coords[1]; - } else { - // System.err.println("Got " + type); - } - i.next(); - } - return Amount.valueOf(len, SI.MILLIMETER); - } - private void findWebThickness() { java.awt.geom.Area a = getCrossSection(Amount.valueOf(0, SI.MILLIMETER)); Rectangle r = a.getBounds(); @@ -199,9 +167,7 @@ public class ExtrudedShapeGrain extends MotorPart implements Grain { @Override public java.awt.geom.Area getCrossSection(Amount regression) { - java.awt.geom.Area res = getPlus(regression); - res.subtract(getMinus(regression)); - return res; + return xsection.getShape(regression); } @Override @@ -214,7 +180,7 @@ public class ExtrudedShapeGrain extends MotorPart implements Grain { double rLenmm = rLen.doubleValue(SI.MILLIMETER); - for( java.awt.geom.Area a : separate(getCrossSection(regression))){ + for( java.awt.geom.Area a : ShapeUtil.separate(getCrossSection(regression))){ Rectangle2D bounds = a.getBounds2D(); Rectangle2D side = new Rectangle2D.Double(bounds.getMinX(), -rLenmm/2.0, bounds.getWidth(), rLenmm); res.add(new java.awt.geom.Area(side)); @@ -222,79 +188,6 @@ public class ExtrudedShapeGrain extends MotorPart implements Grain { return res; } - private java.awt.geom.Area getPlus(Amount regression) { - java.awt.geom.Area a = new java.awt.geom.Area(); - for (Shape s : plus) - a.add(new java.awt.geom.Area(regress(s, regression - .doubleValue(SI.MILLIMETER), true))); - return a; - } - - private java.awt.geom.Area getMinus(Amount regression) { - java.awt.geom.Area a = new java.awt.geom.Area(); - for (Shape s : minus) - a.add(new java.awt.geom.Area(regress(s, regression - .doubleValue(SI.MILLIMETER), false))); - return a; - } - - - private Shape regress(Shape s, double mm, boolean plus) { - if (inhibited.contains(s)) - return s; - if (s instanceof Ellipse2D) { - Ellipse2D e = (Ellipse2D) s; - - double d = plus ? -2 * mm : 2 * mm; - - double w = e.getWidth() + d; - double h = e.getHeight() + d; - double x = e.getX() - d / 2; - double y = e.getY() - d / 2; - - return new Ellipse2D.Double(x, y, w, h); - } else if (s instanceof Rectangle2D) { - Rectangle2D r = (Rectangle2D) s; - - if ( plus ){ - double d = -2 * mm; - double w = r.getWidth() + d; - double h = r.getHeight() + d; - double x = r.getX() - d / 2; - double y = r.getY() - d / 2; - return new Rectangle2D.Double(x, y, w, h); - } else { - //A rectangular hole gets rounded corners as it grows - java.awt.geom.Area a = new java.awt.geom.Area(); - double d = 2 * mm; - - //Make it wider - double w = r.getWidth() + d; - double h = r.getHeight(); - double x = r.getX() - d / 2; - double y = r.getY(); - a.add( new java.awt.geom.Area(new Rectangle2D.Double(x, y, w, h))); - - //Make it taller - w = r.getWidth(); - h = r.getHeight() + d; - x = r.getX(); - y = r.getY() - d / 2; - a.add( new java.awt.geom.Area(new Rectangle2D.Double(x, y, w, h))); - - //Add rounded corners - a.add( new java.awt.geom.Area(new Ellipse2D.Double(r.getX()-mm, r.getY()-mm, mm*2, mm*2))); - a.add( new java.awt.geom.Area(new Ellipse2D.Double(r.getX()+r.getWidth()-mm, r.getY()-mm, mm*2, mm*2))); - a.add( new java.awt.geom.Area(new Ellipse2D.Double(r.getX()+r.getWidth()-mm, r.getY()+r.getHeight()-mm, mm*2, mm*2))); - a.add( new java.awt.geom.Area(new Ellipse2D.Double(r.getX()-mm, r.getY()+r.getHeight()-mm, mm*2, mm*2))); - - return a; - } - - } - return null; - } - public static void main(String args[]) throws Exception { ExtrudedShapeGrain e = new ExtrudedShapeGrain(); new Editor(e).show(); @@ -302,114 +195,6 @@ public class ExtrudedShapeGrain extends MotorPart implements Grain { } - /* - * Separate an area into multiple distinct area. - * Area CAN NOT HAVE HOLES. HOLES WILL BE RETURNED AS AREAS, - * SO A DONUT WILL TURN INTO TWO CIRCLES. - */ - private Set separate(java.awt.geom.Area a) { - Set res = new HashSet(); - PathIterator i = a.getPathIterator(new AffineTransform()); - GeneralPath cur = null; - - while (!i.isDone()) { - double coords[] = new double[6]; - int type = i.currentSegment(coords); - switch (type) { - case PathIterator.SEG_CLOSE: - cur.closePath(); - if (cur != null ){ - java.awt.geom.Area area = new java.awt.geom.Area(cur); - if ( !a.isEmpty() ) - res.add(area); - } - cur = new GeneralPath(i.getWindingRule()); - break; - case PathIterator.SEG_MOVETO: - if (cur != null ){ - java.awt.geom.Area area = new java.awt.geom.Area(cur); - if ( !a.isEmpty() ) - res.add(area); - } - cur = new GeneralPath(i.getWindingRule()); - cur.moveTo(coords[0], coords[1]); - break; - case PathIterator.SEG_CUBICTO: - cur.curveTo(coords[0], coords[1], coords[2], coords[3], - coords[4], coords[5]); - break; - case PathIterator.SEG_LINETO: - cur.lineTo(coords[0], coords[1]); - break; - case PathIterator.SEG_QUADTO: - cur.quadTo(coords[0], coords[1], coords[2], coords[3]); - break; - - } - i.next(); - } - - return res; - } - - /* - * Return the Area of a singular polygon (NO HOLES OR DISJOINT PARTS). - * Coordinates assumed to be in MM. - * http://valis.cs.uiuc.edu/~sariel/research/CG/compgeom/msg00831.html - * http://stackoverflow.com/questions/451426/how-do-i-calculate-the-surface-area-of-a-2d-polygon - * http://www.wikihow.com/Calculate-the-Area-of-a-Polygon - */ - private Amount area(java.awt.geom.Area a) { - if ( !a.isSingular() ) - throw new IllegalArgumentException("Can not calculate area of non-singular shape!"); - PathIterator i = a.getPathIterator(new AffineTransform(), .001); - - - double x = 0, y = 0, sx = 0, sy = 0; - double nx, ny; - double area = 0; - while (!i.isDone()) { - double coords[] = new double[6]; - int type = i.currentSegment(coords); - switch( type ){ - case PathIterator.SEG_CLOSE: - //Go back to the start - nx = sx; - ny = sy; - area += x * ny; - area -= y * nx; - break; - case PathIterator.SEG_LINETO: - nx = coords[0]; - ny = coords[1]; - area += x * ny; - area -= y * nx; - - //Remember the last points - x = nx; - y = ny; - - break; - case PathIterator.SEG_MOVETO: - //Remember the starting point - x = sx = coords[0]; - y = sy = coords[1]; - break; - default: - throw new Error("Bad segment type from Flattening Path Iterator"); - } - i.next(); - } - - area = area / 2.0; // Result so far is double the signed area - - if ( area < 0 ) //Depending on winding it could be negative - area = area * -1.0; - - - return Amount.valueOf(area, SI.MILLIMETER.pow(2)).to(Area.UNIT); - } - public Amount getLength() { return length; } diff --git a/src/com/billkuker/rocketry/motorsim/grain/ShapeUtil.java b/src/com/billkuker/rocketry/motorsim/grain/ShapeUtil.java new file mode 100644 index 0000000..23e8e2d --- /dev/null +++ b/src/com/billkuker/rocketry/motorsim/grain/ShapeUtil.java @@ -0,0 +1,153 @@ +package com.billkuker.rocketry.motorsim.grain; +import java.awt.geom.AffineTransform; +import java.awt.geom.GeneralPath; +import java.awt.geom.PathIterator; +import java.util.HashSet; +import java.util.Set; + +import javax.measure.quantity.Area; +import javax.measure.quantity.Length; +import javax.measure.unit.SI; + +import org.jscience.physics.amount.Amount; +public class ShapeUtil { + private ShapeUtil(){} + + /* + * Return the Area of a singular polygon (NO HOLES OR DISJOINT PARTS). + * Coordinates assumed to be in MM. + * http://valis.cs.uiuc.edu/~sariel/research/CG/compgeom/msg00831.html + * http://stackoverflow.com/questions/451426/how-do-i-calculate-the-surface-area-of-a-2d-polygon + * http://www.wikihow.com/Calculate-the-Area-of-a-Polygon + */ + public static Amount area(java.awt.geom.Area a) { + if ( !a.isSingular() ) + throw new IllegalArgumentException("Can not calculate area of non-singular shape!"); + PathIterator i = a.getPathIterator(new AffineTransform(), .001); + + + double x = 0, y = 0, sx = 0, sy = 0; + double nx, ny; + double area = 0; + while (!i.isDone()) { + double coords[] = new double[6]; + int type = i.currentSegment(coords); + switch( type ){ + case PathIterator.SEG_CLOSE: + //Go back to the start + nx = sx; + ny = sy; + area += x * ny; + area -= y * nx; + break; + case PathIterator.SEG_LINETO: + nx = coords[0]; + ny = coords[1]; + area += x * ny; + area -= y * nx; + + //Remember the last points + x = nx; + y = ny; + + break; + case PathIterator.SEG_MOVETO: + //Remember the starting point + x = sx = coords[0]; + y = sy = coords[1]; + break; + default: + throw new Error("Bad segment type from Flattening Path Iterator"); + } + i.next(); + } + + area = area / 2.0; // Result so far is double the signed area + + if ( area < 0 ) //Depending on winding it could be negative + area = area * -1.0; + + + return Amount.valueOf(area, SI.MILLIMETER.pow(2)).to(Area.UNIT); + } + + public static Amount perimeter(java.awt.geom.Area a) { + //TODO: I think I need to handle seg_close!! + PathIterator i = a.getPathIterator(new AffineTransform(), .001); + double x = 0, y = 0; + double len = 0; + while (!i.isDone()) { + double coords[] = new double[6]; + int type = i.currentSegment(coords); + if (type == PathIterator.SEG_LINETO) { + // System.out.println("Line"); + double nx = coords[0]; + double ny = coords[1]; + // System.out.println(x+","+y+ " to " + nx+"," + ny); + len += Math.sqrt(Math.pow(x - nx, 2) + Math.pow(y - ny, 2)); + x = nx; + y = ny; + } else if (type == PathIterator.SEG_MOVETO) { + // System.out.println("Move"); + x = coords[0]; + y = coords[1]; + } else { + // System.err.println("Got " + type); + } + i.next(); + } + return Amount.valueOf(len, SI.MILLIMETER); + } + + /* + * Separate an area into multiple distinct area. + * Area CAN NOT HAVE HOLES. HOLES WILL BE RETURNED AS AREAS, + * SO A DONUT WILL TURN INTO TWO CIRCLES. + */ + public static Set separate(java.awt.geom.Area a) { + Set res = new HashSet(); + PathIterator i = a.getPathIterator(new AffineTransform()); + GeneralPath cur = null; + + while (!i.isDone()) { + double coords[] = new double[6]; + int type = i.currentSegment(coords); + switch (type) { + case PathIterator.SEG_CLOSE: + cur.closePath(); + if (cur != null ){ + java.awt.geom.Area area = new java.awt.geom.Area(cur); + if ( !a.isEmpty() ) + res.add(area); + } + cur = new GeneralPath(i.getWindingRule()); + break; + case PathIterator.SEG_MOVETO: + if (cur != null ){ + java.awt.geom.Area area = new java.awt.geom.Area(cur); + if ( !a.isEmpty() ) + res.add(area); + } + cur = new GeneralPath(i.getWindingRule()); + cur.moveTo(coords[0], coords[1]); + break; + case PathIterator.SEG_CUBICTO: + cur.curveTo(coords[0], coords[1], coords[2], coords[3], + coords[4], coords[5]); + break; + case PathIterator.SEG_LINETO: + cur.lineTo(coords[0], coords[1]); + break; + case PathIterator.SEG_QUADTO: + cur.quadTo(coords[0], coords[1], coords[2], coords[3]); + break; + + } + i.next(); + } + + return res; + } + + +} -- 2.47.2