diff --git a/src/main/java/cit/PureATN/Note.java b/src/main/java/cit/PureATN/Note.java index 939e288..af4d524 100644 --- a/src/main/java/cit/PureATN/Note.java +++ b/src/main/java/cit/PureATN/Note.java @@ -26,10 +26,11 @@ public static PInputEventFilter disablemask = new PInputEventFilter(0); public static Note theapp; // The Application +public DPenReceiver dPenReceiver; public Note() { this(null); - setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); + setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); theapp = this; // どこからでもNote.theapp で参照できるように } @@ -37,12 +38,15 @@ super("Note", false, aCanvas); setSize(650, 800); setLocation(10,10); + + dPenReceiver = new DPenReceiver(this); } public void initialize() { super.initialize(); // らくがき用マウスイベント + // final PBasicInputEventHandler squiggleEventHandler = createSSSquiggleEventHandler(); final PBasicInputEventHandler squiggleEventHandler = createSquiggleEventHandler(); squiggleEventHandler.setEventFilter(new PInputEventFilter(InputEvent.BUTTON1_MASK)); getCanvas().removeInputEventListener(getCanvas().getPanEventHandler()); @@ -65,20 +69,69 @@ getCanvas().setInteractingRenderQuality(PPaintContext.HIGH_QUALITY_RENDERING); } + // public PBasicInputEventHandler createSSSquiggleEventHandler() { + // return new PBasicInputEventHandler() { + + // protected ShortStroke squiggle; + // int dragCount = 0; + // Point2D pressP; + + // public void mousePressed(final PInputEvent aEvent) { + // pressP = aEvent.getPositionRelativeTo(layer); + // dragCount = 0; + // } + // public void mouseDragged(final PInputEvent aEvent) { + // dragCount++; + // if (squiggle == null) { + // squiggle = new ShortStroke(); + + // squiggle.penid = 1; + // squiggle.paperid = 1; + // squiggle.time = System.currentTimeMillis(); + // squiggle.addPoint(pressP); + // squiggle.applyTempPtsToAry(); + // squiggle.moveTo( pressP.getX(), pressP.getY() ); + // squiggle.setTransparency(0.7f); + // // squiggle.rebuildStroke(); + + // layer.addChild(squiggle); + + // } else { + // Point2D inkp = aEvent.getPositionRelativeTo(layer); + // squiggle.addPoint(inkp); + // squiggle.lineTo( inkp.getX(),inkp.getY()); + // } + // } + // public void mouseReleased(final PInputEvent aEvent) { + + // if (dragCount < 3){ + + // } else { + + // squiggle.play(300); + // squiggle = null; + // } + // } + + // }; + // } + + public PBasicInputEventHandler createSquiggleEventHandler() { return new PDragSequenceEventHandler() { - protected PPath squiggle; + protected ShortStroke squiggle; public void startDrag(final PInputEvent e) { super.startDrag(e); final Point2D p = e.getPosition(); - squiggle = new PPath.Float(); - squiggle.moveTo((float) p.getX(), (float) p.getY()); - squiggle.setStroke(new BasicStroke((float) (1 / e.getCamera().getViewScale()))); + squiggle = new ShortStroke(); layer.addChild(squiggle); + squiggle.startDrag_on_draw(p.getX(), p.getY()); + // squiggle.setStroke(new BasicStroke((float) (3 / e.getCamera().getViewScale()))); + layer.repaint(); } public void drag(final PInputEvent e) { @@ -89,16 +142,22 @@ public void endDrag(final PInputEvent e) { super.endDrag(e); updateSquiggle(e); + squiggle.endDrag_on_draw(); + // squiggle.ink = layer; + // squiggle.play(10); squiggle = null; } public void updateSquiggle(final PInputEvent aEvent) { final Point2D p = aEvent.getPosition(); - squiggle.lineTo((float) p.getX(), (float) p.getY()); + squiggle.drag_on_draw( p.getX(), p.getY()); + layer.repaint(); } }; } + + public static void main(final String[] args) { new Note(); } diff --git a/src/main/java/cit/PureATN/PPPath.java b/src/main/java/cit/PureATN/PPPath.java new file mode 100644 index 0000000..985833d --- /dev/null +++ b/src/main/java/cit/PureATN/PPPath.java @@ -0,0 +1,24 @@ +package cit.PureATN; + +import java.awt.Color; +import java.awt.Shape; +import java.awt.geom.Path2D; + +import org.piccolo2d.nodes.PPath; + +public class PPPath extends PPath.Double { + public PPPath(){ + super(new Path2D.Float()); + // setStrokePaint(DEFAULT_STROKE_PAINT); + // setStroke(DEFAULT_STROKE); + setPaint(null); + } + public void setPathTo(final Shape aShape) { + this.getPath().reset(); + append(aShape, false); + } + // public PInterpolatingActivity animateToStrokeColor(final Color destColor, final long duration) { + // return super.animateToColor(destColor, duration); + + // } +} diff --git a/src/main/java/cit/PureATN/PenUser.java b/src/main/java/cit/PureATN/PenUser.java index 9d53030..05ca594 100644 --- a/src/main/java/cit/PureATN/PenUser.java +++ b/src/main/java/cit/PureATN/PenUser.java @@ -61,7 +61,7 @@ if (ph == 0){ if (lastStroke != null){ lastStroke.finish(); - bundlesend(lastStroke); + // bundlesend(lastStroke); lastStroke = null; } } else { diff --git a/src/main/java/cit/PureATN/ShortStroke.java b/src/main/java/cit/PureATN/ShortStroke.java new file mode 100644 index 0000000..6c48c2d --- /dev/null +++ b/src/main/java/cit/PureATN/ShortStroke.java @@ -0,0 +1,928 @@ +package cit.PureATN; +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Shape; +import java.awt.geom.GeneralPath; +import java.awt.geom.Point2D; +import java.io.BufferedOutputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.Serializable; +import java.lang.reflect.Field; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.Hashtable; +import java.util.NoSuchElementException; +import java.util.StringTokenizer; +import java.util.Vector; + +import org.piccolo2d.PNode; +import org.piccolo2d.activities.PActivity; +import org.piccolo2d.nodes.PPath; +import org.piccolo2d.util.PBounds; + + +/** + * 生徒の筆記(1ストローク)モデル + * @author miuramo + * + */ +public class ShortStroke extends PPPath implements Serializable, Runnable { + private static final long serialVersionUID = 5928762332664417016L; + + //TODO: 筆記解析をするならtrue + public transient static boolean doAssess = false; + + public float[] sx; + public float[] sy; + // public short[] penpress; + public float width; + public int color; + public int penid;//ペンのID + // public int paperid;//シートのID + public long time; + // public int sbackid; + + public transient long id; + public transient ArrayList tempPts; + + public transient PNode ink; + public transient Point2D startp; + public transient Shape full; + + // transient Event_onSS event_onss = null; + + public String toCsv(){ + SimpleDateFormat sdf = new SimpleDateFormat("\"yyyy/MM/dd HH:mm:ss\""); + StringBuffer sb = new StringBuffer(); + sb.append(penid); + sb.append(","); + // sb.append(paperid); + // sb.append(","); + sb.append(String.valueOf(time)); + sb.append(","); + sb.append(sdf.format(time)); + sb.append(","); + sb.append(sx.length); + sb.append(","); + for(int i=0;i shapel; + public transient PBounds bounds; + + static Hashtable penStrokeHash; + static Hashtable colorHash; + + static { + penStrokeHash = new Hashtable(); + colorHash = new Hashtable(); + } + + public static BasicStroke getCachedBasicStroke(float f){ //f=太さ + if (penStrokeHash.containsKey(java.lang.Float.valueOf(f).toString())){ + return penStrokeHash.get(java.lang.Float.valueOf(f).toString()); + } else { + BasicStroke bs = new BasicStroke( f/2, BasicStroke.CAP_ROUND, + BasicStroke.JOIN_ROUND, 90.0f); + penStrokeHash.put( java.lang.Float.valueOf(f).toString(), bs); + return bs; + } + } + public static Color getColor(int f){ + if (colorHash.containsKey(f)){ + return colorHash.get(f); + } else { + Color opaqueC = new Color(f); + Color cl = new Color(opaqueC.getRed(), + opaqueC.getGreen(), opaqueC.getBlue(), 245); //TODO:ここで筆記の透明度を設定.以前は95 + colorHash.put(f, cl); + return cl; + } + } + + // 新規作成 + public ShortStroke() { + this(0, new Color(0,0,90), 3, new Date().getTime()); + } + public ShortStroke(int _penid, Color _color, int _width, long _time){ + penid = _penid; + color = _color.getRGB(); + this.setStrokePaint(_color); + width = _width; + setStroke(getCachedBasicStroke(width)); + time = _time; + setTransparency(0.7f); + playing = false; + } + // startDragのまえに、layer.addChildしておく + public void startDrag_on_draw(double px, double py){ + tempPts = new ArrayList(); + this.moveTo(px,py); + } + public void drag_on_draw(double px, double py){ + tempPts.add(new Point2D.Double(px,py)); + this.lineTo(px,py); + } + public void endDrag_on_draw(){ + applyTempPtsToAry(); + } + public void applyTempPtsToAry(){ + sx = new float[tempPts.size()]; + sy = new float[tempPts.size()]; + // penpress = new short[tempPts.size()]; + for(int i=0;i v = new Vector(); + + int c = st.countTokens(); + int p = 0; + sx = new float[c / 2]; + sy = new float[c / 2]; + + float tsx = (float) (Integer.parseInt(st.nextToken(), 36) * inkscale); + float tsy = (float) (Integer.parseInt(st.nextToken(), 36) * inkscale); + float ttx, tty; // previous values; + sx[p] = tsx; + sy[p] = tsy; + // System.out.println("tsx: "+ tsx + " tsy: "+tsy); + double[] t = new double[2]; + t[0] = tsx; + t[1] = tsy; + v.add(t); + ttx = tsx; + tty = tsy; + while (st.hasMoreElements()) { + p++; + float x = (float) (Integer.parseInt(st.nextToken(), 36) * inkscale); + float y = (float) (Integer.parseInt(st.nextToken(), 36) * inkscale); + sx[p] = x; + sy[p] = y; + if (x != ttx && y != tty) { + double[] tt = new double[2]; + tt[0] = x; + tt[1] = y; + v.add(tt); + } + } + // FitCurves fc = new FitCurves(); + // try { + // fc.getBezier(v, 4.0); + // } catch (ArrayIndexOutOfBoundsException aioobe) { + // genStroke_old(protocol, inkscale); + // } + return this; + } + + public synchronized PPath genStroke_old(String protocol, float inkscale) { + try { + StringTokenizer st = new StringTokenizer(protocol); + time = new Date().getTime(); + // System.out.println(time); + + int c = st.countTokens(); + int p = 0; + sx = new float[c / 2]; + sy = new float[c / 2]; + // stroke = new MyPPath(this); + reset(); + float tsx = (float) (Integer.parseInt(st.nextToken(), 36) * inkscale); + float tsy = (float) (Integer.parseInt(st.nextToken(), 36) * inkscale); + moveTo((float) tsx, (float) tsy); + sx[p] = tsx; + sy[p] = tsy; + while (st.hasMoreElements()) { + p++; + float x = (float) (Integer.parseInt(st.nextToken(), 36) * inkscale); + float y = (float) (Integer.parseInt(st.nextToken(), 36) * inkscale); + lineTo((float) x, (float) y); + sx[p] = x; + sy[p] = y; + } + return this; + } catch (NoSuchElementException ex) { + System.out.println(ex.toString()); + return null; + } + } + + public synchronized PPath rebuildStroke_old() { + // stroke = new MyPPath(this); + + reset(); + if (sx.length == 0 || sy.length == 0) { + System.out.println("長さ0のストロークデータ " + time); + return this; + } + moveTo((float) sx[0], (float) sy[0]); + for (int i = 0; i < sx.length; i++) { + lineTo((float) sx[i], (float) sy[i]); + } + full = new GeneralPath(this.getPathReference()); // 最後までの筆記を記録 + +// removeAllChildren(); +// for(int i=0;i1.0) h=1.0f; +// float b = (float)(Math.log(varspeed+0.01)+0.2); +// if (b>1.0) b=1.0f; +// int hh = (int)(h*205)+50; +// if (hh>255) hh=255; + // setStrokePaint(new Color(Color.HSBtoRGB(h, 0.7f, 0.7f))); + // if (hh>60) setStrokePaint(new Color(hh,hh,hh)); + // else setStrokePaint(new Color(255-hh*2,0,0)); + return rebuildStroke_old(); + // return rebuildStroke_besier(); + } + + public synchronized PPath rebuildStroke_besier() { // bezier version + // float ttx2 = -1, tty2 = 0; + float ttx1 = 0, tty1 = 0; + Vector v = new Vector(); + for (int i = 0; i < sx.length; i++) { + float x = sx[i]; + float y = sy[i]; + if (x != ttx1 && y != tty1) { + double[] tt = new double[2]; + double[] tmid = new double[2]; + if (ttx1 != 0 && tty1 != 0) { + tmid[0] = (x + ttx1) / 2.0; + tmid[1] = (y + tty1) / 2.0; + v.add(tmid); + } + tt[0] = x; + tt[1] = y; + v.add(tt); + } + ttx1 = x; + tty1 = y; + } + // FitCurves fc = new FitCurves(); + // fc.getBezier(v, 4.0); + // ((MyPPath)stroke).setSS(this); + return this; + } + + //自分の最後と、otherの最初の距離をみる + public float distance(ShortStroke other){ + if (sx.length==0) return 99999f; + float tx = sx[sx.length-1]; + float ty = sy[sy.length-1]; + if (other.sx.length==0) return 99999f; + float ox = other.sx[0]; + float oy = other.sy[0]; + return (float) Math.sqrt((ox-tx)*(ox-tx)+(oy-ty)*(oy-ty)); + + } + public void move(float dx, float dy){//移動 + for(int i=0;i(); + } + Shape shape = this.getPathReference(); + shapel.add(new GeneralPath(shape)); + } + + // リセット + public void resetShapes() { + if (full == null) { + rebuildStroke(); + } + if (shapel == null) { + shapel = new ArrayList(); + } + setPathTo(full); + shapel.clear(); + } + + // 形状を保存 + public void prepareShapes() { + if (shapel == null) { + shapel = new ArrayList(); + } + shapel.clear(); + GeneralPath gp = new GeneralPath(); + gp.moveTo(sx[0], sy[0]); + shapel.add(new GeneralPath(gp)); + for (int i = 1; i < sx.length; i++) { + gp.lineTo(sx[i], sy[i]); + shapel.add(new GeneralPath(gp)); + } + } + + public int getSteps() { + return sx.length; + } + //TODO: + public transient double[] speedary; + public transient double[] speedary2; + public transient double dist = -1; + public transient double length2; + public transient double avgspeed; + public transient double avgspeed2; + public transient double varspeed; + public transient double varspeed2; + public transient double maxspeed; + public transient ShortStroke previous; + public transient long timediff; + public transient long timed; + public transient double[] angleary ; + public transient double[] angleary2 ; + public transient double[] angleary3 ; + public transient int num_choten = 0;//90度以上の点の数 + public transient int num_choten2 = 0;//90度以上の点の数 ノイズ除去ver + public transient double sumangle, avgangle; + public transient double varangle; + public transient int ramer; + public transient double choten1_var; + public transient double choten1_avg; + public transient double choten2_var; + public transient double choten2_avg; + public transient int kukan2; + public transient double var_oreten; + public transient double var_ramer; + public transient double ramerthre; + public transient double ppd, pps,ppr; + public transient double pts, dbltime; + + public int pos; //水井筆記のpos + public String tag; + + public void print_metrics2(){ + String a[] = new String[]{"var_ramer","ppd","pts","length", "current_begin"}; + for(String fn: a){ + Field f = null; + try { + f = ShortStroke.class.getField(fn); + } catch (NoSuchFieldException e) { + e.printStackTrace(); + } catch (SecurityException e) { + e.printStackTrace(); + } + + try { + System.out.println(fn+" : "+f.get(this)); + } catch (IllegalArgumentException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + System.out.println(""); + } + public void print_metrics(){ + ShortStroke clone = this.clone(); + clone.calcMetrics(); + String a[] = new String[]{"ramerthre","varspeed2","numrammer","var_ramer", "length"}; + for(String fn: a){ + Field f = null; + try { + f = ShortStroke.class.getField(fn); + } catch (NoSuchFieldException e) { + e.printStackTrace(); + } catch (SecurityException e) { + e.printStackTrace(); + } + + try { + System.out.println(fn+" : "+f.get(clone)); + } catch (IllegalArgumentException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + System.out.println("size : "+clone.sx.length); + for(double d: clone.speedary) System.out.print(d+" "); + System.out.println(""); + } + private void log() { + var_oreten = varspeed /( Math.log10(num_choten2+2)); + var_ramer = varspeed / (Math.log10(ramer+2)); + ppd = sx.length/dist; + pps = sx.length/(ramerthre*5); + ppr = sx.length/(ramer+1); + pts = sx.length; + dbltime = (double)(time - 1360901083972L)/1000; +// System.out.println("Math.log10 = "+Math.log10(numrammer+2)); + } + + private void ramer() { + float minx=java.lang.Float.MAX_VALUE,maxx=java.lang.Float.MIN_VALUE,miny=java.lang.Float.MAX_VALUE,maxy=java.lang.Float.MIN_VALUE; + for(int i=0;i maxx) maxx = sx[i]; + if (sy[i] < miny) miny = sy[i]; + if (sy[i] > maxy) maxy = sy[i]; + } + double w = maxx-minx; + double h = maxy-miny; + ramerthre = (w > h) ? w/5 : h/5; + //TODO: ramer = Rammer.getRammerPoints(sx, sy, (float)ramerthre).size()-2; + } + + private void angle() { + if (sx.length > 2) { + angleary = new double[sx.length-2]; + num_choten = 0; + //1または2か所の合計で80以上曲がっている折れ点の数を数える。 + double prev_angle = -1.0; + boolean isprev_choten = false; + for(int i=0;i -0.17) { + num_choten++; + isprev_choten = true; + } + else if ((cos + prev_angle > -1.53) && isprev_choten==false) {//(cos > -0.8) && (prev_angle > -0.8) + num_choten++; + isprev_choten = true; + } else { + isprev_choten = false; + } + sumangle += cos; + prev_angle = cos; + } + } + // 初めの二つの角度について除外 + if(sx.length > 4){ + angleary2 = new double[sx.length-4]; + double prev_angle2 = -1.0; + boolean isprev_choten2 = false; + num_choten2 = 0; + for(int i=0;i -0.17) { + num_choten2++; + isprev_choten2 = true; + } + else if ((cos2 + prev_angle2 > -1.53) && isprev_choten2==false) {//(cos > -0.8) && (prev_angle > -0.8) + num_choten2++; + isprev_choten2 = true; + } else { + isprev_choten2 = false; + } + prev_angle2 = cos2; + } + //折れ点の数をノイズ分ひく + if(angleary2.length > 0){ + if(angleary2[angleary2.length-1] > -0.17 && speedary[speedary.length-1] < 1.0){ + num_choten2--; + } + } + if(angleary2.length > 1){ + if(angleary2[angleary2.length-2] > -0.17 && (speedary[speedary.length-1]+speedary[speedary.length-2]) < 2.0){ + num_choten2--; + } + } + } + } + + public void setPreviousSS(ShortStroke pre){ + if (pre == null) return; + if (pre.penid != this.penid) return; + // if (pre.paperid != this.paperid) return; + if (pre.id == this.id-1) { + previous = pre; + timediff = this.time - previous.time; + long pretime = ((previous.sx.length-1)*1000)/75; + timed = timediff - pretime; + } + } + //筆記のパスの長さ(これをgetSteps()で割ったら平均速度になる) + public double lengthInPixel(){ + double len = 0; + double len2 = 0; + if (sx.length>1){ + speedary = new double[sx.length-1]; + for(int i=1;i 3) { + speedary2 = new double[sx.length-3]; + for(int i=3;i 4 ) { + for(int i=0;i 1){ + + if(angleary3[angleary3.length-1] > -0.17 && speedary[speedary.length-1] < 1.0){ +// System.out.println("ts"); + len2 = len2 - speedary2[speedary2.length-1]; + kukan2 = kukan2 - 1; + } + } + if(angleary3.length > 2){ + if(angleary3[angleary3.length-2] > -0.17 && (speedary[speedary.length-1]+speedary[speedary.length-2]) < 2.0){ + len2 = len2 - speedary2[speedary2.length-1] - speedary2[speedary2.length-2]; + kukan2 = kukan2 - 2; + } + } + } + dist = len; + length2 = len2; + return len; + } + + public double maxspeedInPixel(){ //最大瞬間速度 + double len = 0; + double tmp = 0; + if (sx.length >1){ + if (speedary == null) lengthInPixel(); + for(int i=0;i1){ + if (speedary == null) lengthInPixel(); + avgspeed = dist/speedary.length; //平均速度 + for(int i=0;i3){ + + if (speedary2 == null) lengthInPixel(); + avgspeed2 = length2/kukan2;//平均速度 + for(int i=0;i