고천문기기복원수업 혼상 제작기.

재연·2022년 12월 8일
0

필요한거

목록 보기
2/2

1. 혼상?

2. 온쉐이프(Onshape)

  • 브라우저에서 캐드 작업을 할 수 있도록 설계된 소프트웨어입니다.
  • 솔리드웍스와 대체로 내용이나 UI가 비슷합니다.
  • 학생의 경우 학생 할인으로, 무료로 작업할 수 있습니다!

3. 피처스크립트(FeatureScript)

  • 온쉐이프에서 사용할 수 있는, 스케치, 솔리드, 점, 선, 면, 바디 등 다양한 피쳐를 다룰 수 있도록 설계된 프로그래밍 언어입니다.
  • 사용해 본 바로는, 자바스크립트와 굉장히 닮아 있으므로 자바스크립트를 사용해 보신 적이 있으시다면 편하게 접근 가능할 것 같아요.
  • 장점이자 단점으로는, 이게 들어오는 순간 이미 캐드가 캐드가 아니게 됩니다. 돌출? 회전? 쉘? 필렛? 거의 까먹었어요.... 코드만 잘 짜 두면 사실상 좌표 정리만 남게 됩니다.
  • 또 하나 아주 중요한 단점으로는 사용하는 사람의 인구가 적어서 아주 기본적인 질의응답만 참조할 수 있었습니다. 스택오버플로우와는 급이 달랐습니다.
  • 선형대수 지식....이 필요합니다.

4. 복원된 혼상


온쉐이프

4.1 코드

//별자리의 첫번째, 또는 첫번째 두번째 별(헤드)

FeatureScript 1913;
import(path : "onshape/std/geometry.fs", version : "1913.0");

annotation { "Feature Type Name" : "ConstelLinesHead" }
export const starLinePlane = defineFeature(function(context is Context, id is Id, definition is map)
    precondition
    {
        
        annotation { "Name" : "Ra_1" }
        isAngle(definition.jeokGyeong1, ANGLE_360_BOUNDS);
        
        annotation { "Name" : "Dec_1" }
        isAngle(definition.geoGeuk1, ANGLE_360_BOUNDS);
        
        annotation { "Name" : "Mag_1" }
        isLength(definition.magnitude1, LENGTH_BOUNDS);
        
        annotation { "Name" : "Ra_2" }
        isAngle(definition.jeokGyeong2, ANGLE_360_BOUNDS);
        
        annotation { "Name" : "Dec_2" }
        isAngle(definition.geoGeuk2, ANGLE_360_BOUNDS);
        
        annotation { "Name" : "Mag_2" }
        isLength(definition.magnitude2, LENGTH_BOUNDS);
        
    }
    {
        
        var radius150 = 150 * millimeter;
        
        var x1 = radius150 * sin(definition.geoGeuk1) * cos(definition.jeokGyeong1);
        var y1 = radius150 * sin(definition.geoGeuk1) * sin(definition.jeokGyeong1);
        var z1 = radius150 * cos(definition.geoGeuk1);
        
        var ex1 = (radius150+2*millimeter) * sin(definition.geoGeuk1) * cos(definition.jeokGyeong1);
        var ey1 = (radius150+2*millimeter) * sin(definition.geoGeuk1) * sin(definition.jeokGyeong1);
        var ez1 = (radius150+2*millimeter) * cos(definition.geoGeuk1);

        var vec1 = vector(ex1, ey1, ez1);
        
        var magRadius1 = (4 * millimeter - definition.magnitude1/2) ;
        
        fCylinder(context, id + "cylinder1", {
                "topCenter" : vector(0, 0, 0) * millimeter,
                "bottomCenter" : vec1,
                "radius" : magRadius1,
                "endBound" : BoundingType.THROUGH_ALL,
        });
        
        if (definition.jeokGyeong2 != 30*degree){
            
            var x2 = radius150 * sin(definition.geoGeuk2) * cos(definition.jeokGyeong2);
            var y2 = radius150 * sin(definition.geoGeuk2) * sin(definition.jeokGyeong2);
            var z2 = radius150 * cos(definition.geoGeuk2);
            
            var ex2 = (radius150+2*millimeter) * sin(definition.geoGeuk2) * cos(definition.jeokGyeong2);
            var ey2 = (radius150+2*millimeter) * sin(definition.geoGeuk2) * sin(definition.jeokGyeong2);
            var ez2 = (radius150+2*millimeter) * cos(definition.geoGeuk2);
    
            var vec2 = vector(ex2, ey2, ez2);
            
            var magRadius2 = (4 * millimeter - definition.magnitude2/2) ;
            
            fCylinder(context, id + "cylinder2", {
                    "topCenter" : vector(0, 0, 0) * millimeter,
                    "bottomCenter" : vec2,
                    "radius" : magRadius2,
                    "endBound" : BoundingType.THROUGH_ALL,
            });
            
            var angle = acos(((x1*x2+y1*y2+z1*z2)/radius150^2));
            var normalVec = vector(y1*z2-z1*y2, z1*x2-x1*z2, x1*y2-y1*x2);
            var constelLinePlane is Plane = plane(WORLD_ORIGIN, normalVec, vec1);
            var radius = radius150 / millimeter;
        
            var sketch1 = newSketchOnPlane(context, id + "sketch1", {
                    "sketchPlane" : constelLinePlane
            });
            
            skArc(sketch1, "constelLine", {
                    "start" : vector((radius+1), 0) * millimeter,
                    "mid" : vector((radius+1)*cos(angle/2), (radius+1)*sin(angle/2)) * millimeter,
                    "end" : vector((radius+1)*cos(angle), (radius+1)*sin(angle))*millimeter
            });
            
            skLineSegment(sketch1, "vec1", {
                    "start" : vector(0, 0) * millimeter,
                    "end" : vector((radius+1), 0) * millimeter
            });
            
            skLineSegment(sketch1, "vec2", {
                    "start" : vector(0, 0) * millimeter,
                    "end" : vector((radius+1)*cos(angle), (radius+1)*sin(angle)) * millimeter
            });
            
            skSolve(sketch1);
            
            var regionToExtrude = qContainsPoint(qSketchRegion(id + "sketch1"), WORLD_ORIGIN);
            debug(context, regionToExtrude);
            
            opThicken(context, id + "thicken1", {
                    "entities" : regionToExtrude,
                    "thickness1" : 0.5* millimeter,
                    "thickness2" : 0.5 * millimeter,
                    "endBound" : BoundingType.THROUGH_ALL,
                    
            });
            
            opFillet(context, id + "fillet2", {
                        "entities" : qCreatedBy(id + "cylinder2", EntityType.EDGE),
                        "radius" : magRadius2
                    });
            
            opFillet(context, id + "fillet3", {
                        "entities" : qCreatedBy(id + "thicken1", EntityType.EDGE),
                        "radius" : 0.5*millimeter
                    });
            
        }
        
        opFillet(context, id + "fillet1", {
                    "entities" : qCreatedBy(id + "cylinder1", EntityType.EDGE),
                    "radius" : magRadius1
                });
            
    });
//눈금선 그리기

FeatureScript 1913;
import(path : "onshape/std/geometry.fs", version : "1913.0");

annotation { "Feature Type Name" : "scale_maker" }
export const myFeature = defineFeature(function(context is Context, id is Id, definition is map)
    precondition
    {
        annotation { "Name" : "Face", "Filter" : EntityType.FACE }
        definition.face is Query;
        
    }
    {
        //var normalVec = definition.face.normal;
        //var xAxis = evLine(context, {"edge": definition.direction}).direction;
        
        var skPlane = evPlane(context, {"face": definition.face});
        print(skPlane.normal);
        
        //var skPlane = 
        
        
            var sketch1 = newSketchOnPlane(context, id + "sketch1", {
                "sketchPlane" : skPlane
            });
        
        //var origin = vector(skPlane.origin)
        
        
        for (var i = 0; i < 360; i += 1)
        {
            var angle = i * degree;
            var x_x;
            var y_x;
            var xstart_x;
            var ystart_x;
            var x_y;
            var y_y;
            var xstart_y;
            var ystart_y;
            if (i % 30 == 0) {
                x_x = 180 * cos(angle);
                y_x = 180 * sin(angle)+1* cos(angle);
                xstart_x = 160 * cos(angle);
                ystart_x = 160 * sin(angle)+1* cos(angle);
                x_y = 180 * cos(angle)+1 * sin(angle);
                y_y = 180 * sin(angle);
                xstart_y = 160 * cos(angle)+1 * sin(angle);
                ystart_y = 160 * sin(angle);
            } else if(i % 10 == 0) {
                x_x = 175 * cos(angle);
                y_x = 175 * sin(angle)+1* cos(angle);
                xstart_x = 160 * cos(angle);
                ystart_x = 160 * sin(angle)+1* cos(angle);
                x_y = 175 * cos(angle)+1 * sin(angle);
                y_y = 175 * sin(angle);
                xstart_y = 160 * cos(angle)+1 * sin(angle);
                ystart_y = 160 * sin(angle);
            } else {
                x_x = 170 * cos(angle);
                y_x = 170 * sin(angle)+1* cos(angle);
                xstart_x = 160 * cos(angle);
                ystart_x = 160 * sin(angle)+1* cos(angle);
                x_y = 170 * cos(angle)+1 * sin(angle);
                y_y = 170 * sin(angle);
                xstart_y = 160 * cos(angle)+1 * sin(angle);
                ystart_y = 160 * sin(angle);
            }
            
            
            skLineSegment(sketch1, "vec1" ~ toString(i), {
                    "start" : vector(xstart_x, ystart_x) * millimeter,
                    "end" : vector(x_x, y_x) * millimeter
            });
            skLineSegment(sketch1, "vec2" ~ toString(i), {
                    "start" : vector(x_x, y_x) * millimeter,
                    "end" : vector(x_y, y_y) * millimeter
            });
            skLineSegment(sketch1, "vec3" ~ toString(i), {
                    "end" : vector(xstart_y, ystart_y) * millimeter,
                    "start" : vector(x_y, y_y) * millimeter
            });
            skLineSegment(sketch1, "vec4" ~ toString(i), {
                    "start" : vector(xstart_y, ystart_y) * millimeter,
                    "end" : vector(xstart_x, ystart_x) * millimeter
            });
            
            
        }
        
        
        
        var sketch_cir = newSketchOnPlane(context, id + "sketch_cir", {
                "sketchPlane" : skPlane
        });
        
        skCircle(sketch_cir, "circle1", {
                "center" : vector(0, 0) * millimeter,
                "radius" : 170 * millimeter
        });
        
        skCircle(sketch_cir, "circle1_t", {
                "center" : vector(0, 0) * millimeter,
                "radius" : 171 * millimeter
        });
        
        skCircle(sketch_cir, "circle2", {
                "center" : vector(0, 0) * millimeter,
                "radius" : 175 * millimeter
        });
        
        skCircle(sketch_cir, "circle2_t", {
                "center" : vector(0, 0) * millimeter,
                "radius" : 176 * millimeter
        });
        
        skCircle(sketch_cir, "circle3", {
                "center" : vector(0, 0) * millimeter,
                "radius" : 180 * millimeter
        });
        
        skCircle(sketch_cir, "circle3_t", {
                "center" : vector(0, 0) * millimeter,
                "radius" : 181 * millimeter
        });
        
        
        skSolve(sketch1);
        skSolve(sketch_cir);
        
            opExtrude(context, id + "extrude1", {
                "entities" : qSketchRegion(id + "sketch1", true),
                "direction" : skPlane.normal,
                "endBound" : BoundingType.BLIND,
                "endDepth" : 1*millimeter
                });

    });

4.2 특징

  • <<성경>> 에 기록되어 있는 별 중 근남극성을 제외한 1,319개의 별에 대해서 좌표값, 별의 밝기를 받아와 그대로 구현(피처스크립트 코드 참조)했습니다.
  • 3D프린팅을 할 것을 가정하고 사이즈는 기존 자료를 따르지 않고 30센티미터 정도로 제작했습니다.(8등분 해서 프린팅한 후 접착하는 것을 가정했습니다.)
  • 30도마다 위도선, 경도선을 표기했습니다.
  • 자오환과 지평환은 360도, 10도, 30도마다 눈금을 표시했습니다.
  • 위도는 서울이 아니라 대전 기준(36.4도)으로 만들었습니다.

4.3 아쉬운 점

  • 다리를 아직 만들지 못했습니다. 만들기 어려워서보다는 더이상 파츠를 추가할 수 없을 정도로 렉이 너무 심해서, 기본 파츠 구현에 치중했습니다.
  • 눈금선의 경우 음각을 해야 했는데 양각입니다. 눈금선 자체도 피처스크립트로 작업했고, 눈금선을 '제거' 하기를 구현하려고 했지만 제거 기능을 찾을 수 없어서 '돌출'하기로 우선은 처리했습니다.
  • 눈금선에 글자를 표현하지 않았습니다.

4.4 .STL 파일 활용 계획

  • 3D프린터 인쇄
  • 완성된 자료는 웹 3D로 최적화된 파일 포맷인 .gltf로 내보내고 텍스처를 추가하여 온라인 게임 아이템으로서 활용할 계획입니다. 앞으로 취미삼아 다른 천문의기도 제작해 보고 싶습니다!
profile
영어의 권력에 저항해요

0개의 댓글