flutter下嘗試3d建模

library flutter_rubic;

import 'dart:io';
import 'dart:math' as Math;
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart' show rootBundle;
import 'package:flutter/widgets.dart';
import 'package:meta/meta.dart';
import 'package:triangle/cube.dart';
import 'package:vector_math/vector_math.dart' show Vector3;
import 'package:vector_math/vector_math.dart' as V;

class Cubic3D extends StatefulWidget {

  Cubic3D(
      { @required this.size,
        @required this.path,
        @required this.asset,
        this.angleX,
        this.angleY,
        this.angleZ,
        this.zoom = 100.0, this.blocks}) {
    if (angleX != null || angleY != null || angleZ != null) {
      useInternal = false;
    }
    specmode = 1;
  }

  int specmode;
  List<Block> blocks;
  Size size;
  bool asset;
  String path;
  double zoom;
  double angleX;
  double angleY;
  double angleZ;
  bool useInternal = true;

  @override
  _Cubic3DState createState() => new _Cubic3DState(path, useInternal, asset);
}

//class reflect extends Reflectable {
//
//}

class _Cubic3DState extends State<Cubic3D> {
  bool useInternal;
  String path;

  double angleX = 15.0;
  double angleY = 45.0;
  double angleZ = 0.0;

  double _previousX = 0.0;
  double _previousY = 0.0;

  double zoom;
  String object = "V 1 1 1 1";

  Map parsedFile;//= _parseObjString(object);
  File file;

  _Cubic3DState(this.path, this.useInternal, bool asset) {
    if(asset){
      rootBundle.loadString(this.path).then((String value) {
        setState(() {
          object = value;
          parsedFile = _parseObjString(object);
        });
      });
    }else{
      File file = new File(this.path);
      file.readAsString().then((String value) {
        setState(() {
          object = value;
          parsedFile = _parseObjString(object);
        });
      });
    }
  }


  Map<String, List> _parseObjString(String objString) {
    List vertices = <Vector3>[];
    List faces = <List<int>>[];
    List usemtl = <Color>[];
    List<int> face = [];

    List<String> lines = objString.split("\n");
    var lastUseml;
    Vector3 vertex;

    var colortbl = {"red":Colors.red, "blue":Colors.blue, "yellow":Colors.yellow,
      "green":Colors.green, "white": Colors.white, "brown":Colors.brown, "":Colors.transparent};

    lines.forEach((String line) {
      line = line.replaceAll(new RegExp(r"\s+$"), "");
      List<String> chars = line.split(" ");

      // vertex
      if (chars[0] == "v") {
        vertex = new Vector3(double.parse(chars[1]), double.parse(chars[2]),
            double.parse(chars[3]));

        vertices.add(vertex);

        // face
      } else if (chars[0] == "f") {
        for (var i = 1; i < chars.length; i++) {
          face.add(int.parse(chars[i].split("/")[0]));
        }

        faces.add(face);
        face = [];
        usemtl.add(lastUseml);
        if(lastUseml!=null) {
          lastUseml = null;
        }
      } else if(chars[0] == "usemtl") {
        if(!colortbl.containsKey(chars[1])) {
          lastUseml=colortbl[""];
        }
        else {
          lastUseml=colortbl[chars[1]];
        }
      }
    });
    return {'vertices': vertices, 'faces': faces, 'usemtl': usemtl};
  }

  void _updateCube(DragUpdateDetails data) {
    if (angleY > 360.0) {
      angleY = angleY - 360.0;
    }
    if (_previousY > data.globalPosition.dx) {
      setState(() {
        angleY = angleY - 1;
      });
    }
    if (_previousY < data.globalPosition.dx) {
      setState(() {
        angleY = angleY + 1;
      });
    }
    _previousY = data.globalPosition.dx;

    if (angleX > 360.0) {
      angleX = angleX - 360.0;
    }
    if (_previousX > data.globalPosition.dy) {
      setState(() {
        angleX = angleX - 1;
      });
    }
    if (_previousX < data.globalPosition.dy) {
      setState(() {
        angleX = angleX + 1;
      });
    }
    _previousX = data.globalPosition.dy;
  }

  void _updateY(DragUpdateDetails data) {
      _updateCube(data);
  }

  void _updateX(DragUpdateDetails data) {
    _updateCube(data);
  }

  void _touchDown( data) {
    setState(() {
      widget.specmode = -1;
    });
  }

  void _touchUp(PointerUpEvent data) {
    setState(() {
      widget.specmode = 1;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Listener(
      child: new GestureDetector(
        child: new CustomPaint(
          painter: new _ObjectPainter(
              widget.size,
              parsedFile,
              useInternal ? angleX : widget.angleX,
              useInternal ? angleY : widget.angleY,
              useInternal ? angleZ : widget.angleZ,
              widget.zoom, widget.specmode),
          size: widget.size,
        ),
        onHorizontalDragUpdate: _updateY,
        onVerticalDragUpdate: _updateX,
      ),
      onPointerDown: _touchDown,
      onPointerUp: _touchUp,
    );
  }
}

class _ObjectPainter extends CustomPainter {
  double _zoomFactor = 100.0;

  final double _rotation = 5.0; // in degrees
  double _translation = 0.1 / 100;
  final double _scalingFactor = 10.0 / 100.0; // in percent

  final double ZERO = 0.0;

  double _viewPortX = 0.0, _viewPortY = 0.0;

  List<Vector3> vertices;
  List<dynamic> faces;
  List<dynamic> usemtls;
  V.Matrix4 T;
  Vector3 camera;
  Vector3 light;

  double angleX;
  double angleY;
  double angleZ;

  Color color;

  Size size;

  var parsedFile;

  var specmode;

  _ObjectPainter(this.size, this.parsedFile, this.angleX, this.angleY, this.angleZ,
      this._zoomFactor, this.specmode ) {
    _translation *= _zoomFactor;
    camera = new Vector3(0.0, 0.0, 200.0);
    light = new Vector3(0.0, 0.0, 100.0);
    color = new Color.fromARGB(255, 255, 255, 255);
    _viewPortX = (size.width / 2).toDouble();
    _viewPortY = (size.height / 2).toDouble();
  }


  bool _shouldDrawFace(List face, Map avg) {
    return avg["side"]>0;
  }

  Vector3 _normalVector3(Vector3 first, Vector3 second, Vector3 third) {
    Vector3 secondFirst = new Vector3.copy(second);
    secondFirst.sub(first);
    Vector3 secondThird = new Vector3.copy(second);
    secondThird.sub(third);

    return new Vector3(
        (secondFirst.y * secondThird.z) - (secondFirst.z * secondThird.y),
        (secondFirst.z * secondThird.x) - (secondFirst.x * secondThird.z),
        (secondFirst.x * secondThird.y) - (secondFirst.y * secondThird.x));
  }

  double _scalarMultiplication(Vector3 first, Vector3 second) {
    return (first.x * second.x) + (first.y * second.y) + (first.z * second.z);
  }

  Vector3 _calcDefaultVertex(Vector3 vertex) {
    T = new V.Matrix4.translationValues(_viewPortX, _viewPortY, ZERO);
    T.scale(_zoomFactor, -_zoomFactor);

    T.rotateX(_degreeToRadian(angleX != null ? angleX : 0.0));
    T.rotateY(_degreeToRadian(angleY != null ? angleY : 0.0));
    T.rotateZ(_degreeToRadian(angleZ != null ? angleZ : 0.0));

    return T.transform3(vertex);
  }

  double _degreeToRadian(double degree) {
    return degree * (Math.pi / 180.0);
  }

  List<dynamic> _drawFace(List<Vector3> verticesToDraw, List<int> face, Color usemtl) {
    List<dynamic> list = <dynamic>[];
    Paint paint = new Paint();
    Vector3 normalizedLight = new Vector3.copy(light).normalized();

    var normalVector = _normalVector3(verticesToDraw[face[0] - 1],
        verticesToDraw[face[1] - 1], verticesToDraw[face[2] - 1]);

    Vector3 jnv = new Vector3.copy(normalVector).normalized();

    double koef = _scalarMultiplication(jnv, normalizedLight);

    if (koef < 0.0) {
      koef = -koef;
    }
    koef = koef*4/5+0.2;

    Color newColor = usemtl; // = new Color.fromARGB(255, 0, 0, 0);

    Path path = new Path();
    Path bgpath = new Path();

//    newColor = newColor.withRed((usemtl.red.toDouble() * koef).round());
//    newColor = newColor.withGreen((usemtl.green.toDouble() * koef).round());
//    newColor = newColor.withBlue((usemtl.blue.toDouble() * koef).round());

    paint.color = newColor;
    paint.style = PaintingStyle.fill;

    bool lastPoint = false;
    double firstVertexX, firstVertexY, secondVertexX, secondVertexY;
    double centX=0, centY=0;
    if(face.length==4) {
      centX = (verticesToDraw[face[0] - 1][0].toDouble()+verticesToDraw[face[2] - 1][0].toDouble())/2;
      centY = (verticesToDraw[face[0] - 1][1].toDouble()+verticesToDraw[face[2] - 1][1].toDouble())/2;
    }
    for (int i = 0; i < face.length; i++) {
      if (i + 1 == face.length) {
        lastPoint = true;
      }

      if (lastPoint) {
        firstVertexX = verticesToDraw[face[i] - 1][0].toDouble();
        firstVertexY = verticesToDraw[face[i] - 1][1].toDouble();
        secondVertexX = verticesToDraw[face[0] - 1][0].toDouble();
        secondVertexY = verticesToDraw[face[0] - 1][1].toDouble();
      } else {
        firstVertexX = verticesToDraw[face[i] - 1][0].toDouble();
        firstVertexY = verticesToDraw[face[i] - 1][1].toDouble();
        secondVertexX = verticesToDraw[face[i + 1] - 1][0].toDouble();
        secondVertexY = verticesToDraw[face[i + 1] - 1][1].toDouble();
      }
      if (i == 0) {
        bgpath.moveTo(firstVertexX, firstVertexY);
      }

      bgpath.lineTo(secondVertexX, secondVertexY);

      firstVertexX += (centX-firstVertexX)/10;
      firstVertexY += (centY-firstVertexY)/10;
      secondVertexX += (centX-secondVertexX)/10;
      secondVertexY += (centY-secondVertexY)/10;

      if (i == 0) {
        path.moveTo(firstVertexX, firstVertexY);
      }

      path.lineTo(secondVertexX, secondVertexY);
    }
    var z = 0.0;
    face.forEach((int x) {
      z += verticesToDraw[x - 1].z;
    });

    bgpath.close();
    path.close();
    list.add(path);
    list.add(bgpath);
    list.add(paint);
    return list;
  }

  @override
  void paint(Canvas canvas, Size size) {
    if(parsedFile==null) return;
    vertices = parsedFile["vertices"];
    faces = parsedFile["faces"];
    usemtls = parsedFile["usemtl"];

    List<Vector3> verticesToDraw = [];
    vertices.forEach((vertex) {
      verticesToDraw.add(new Vector3.copy(vertex));
    });

    for (int i = 0; i < verticesToDraw.length; i++) {
      verticesToDraw[i] = _calcDefaultVertex(verticesToDraw[i]);
    }

    final List<Map> avgOfZ = <Map>[];
    for (int i = 0; i < faces.length; i++) {
      List<int> face = faces[i];

      var positiveside =
          -(verticesToDraw[face[3] - 1].y-verticesToDraw[face[2] - 1].y)*
          (verticesToDraw[face[2] - 1].x-verticesToDraw[face[1] - 1].x);
     if(positiveside == 0) {
       positiveside = -(verticesToDraw[face[2] - 1].y-verticesToDraw[face[1] - 1].y)*
               (verticesToDraw[face[1] - 1].x-verticesToDraw[face[0] - 1].x);
     }
//      positiveside*=specmode;
      double z = 0.0;
      face.forEach((int x) {
        z += verticesToDraw[x - 1].z;
      });

      Map data = <String, dynamic>{
        "index": i,
        "z": z,
        "side": positiveside,
      };
      avgOfZ.add(data);
    }
    avgOfZ.sort((Map a, Map b) => a['z'].compareTo(b['z']));

    Paint bgpaint = new Paint();
    bgpaint.color = Colors.black;
    bgpaint.style = PaintingStyle.fill;

    Paint trpaint = new Paint();
    trpaint.color = Color.fromARGB(100,200,200,200);
    trpaint.style = PaintingStyle.stroke;

    for (int i = 0; i < faces.length; i++) {
      var ind = avgOfZ[i]["index"];
      List face = faces[ind];
      if (_shouldDrawFace(face, avgOfZ[i]) || specmode==-1) {
        Color fcolor = usemtls[ind];
        if(specmode==-1 && avgOfZ[i]['side']>0) {
          fcolor = Color.fromARGB(50,255,255,255);
        }
        final List<dynamic> faceProp = _drawFace(verticesToDraw, face, fcolor);
        if(specmode==-1 && avgOfZ[i]['side']>0) {
          canvas.drawPath(faceProp[1], trpaint);
        }
        else {
          canvas.drawPath(faceProp[1], bgpaint);
          canvas.drawPath(faceProp[0], faceProp[2]);
        }
      }
    }
  }

  @override
  bool shouldRepaint(_ObjectPainter old) =>
      old.parsedFile != this.parsedFile ||
          old.angleX != angleX ||
          old.angleY != angleY ||
          old.angleZ != angleZ ||
          old._zoomFactor != _zoomFactor ||
          old.specmode != specmode;
}
截圖
標題

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章