585 lines
48 KiB
HTML
585 lines
48 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<title>suportCititor</title>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
|
|
<meta name="generator" content="FreeCAD 1.0.1">
|
|
<style>
|
|
* {
|
|
margin: 0;
|
|
padding: 0;
|
|
}
|
|
body {
|
|
background: #ffffff; /* Old browsers */
|
|
background: -moz-linear-gradient(top, #e3e9fc 0%, #ffffff 70%, #e2dab3 100%); /* FF3.6-15 */
|
|
background: -webkit-linear-gradient(top, #e3e9fc 0%,#ffffff 70%,#e2dab3 100%); /* Chrome10-25, Safari5.1-6 */
|
|
background: linear-gradient(to bottom, #e3e9fc 0%,#ffffff 70%,#e2dab3 100%); /* W3C, IE10+, FF16+, Chrome26+, Opera12+, Safari7+ */
|
|
width: 100vw;
|
|
height: 100vh;
|
|
}
|
|
canvas { display: block; }
|
|
#mainCanvas {
|
|
width: 100%;
|
|
height: 100%;
|
|
}
|
|
#arrowCanvas {
|
|
position: absolute;
|
|
left: 0px;
|
|
bottom: 0px;
|
|
width: 150px;
|
|
height: 150px;
|
|
z-index: 100;
|
|
}
|
|
select { width: 170px; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<canvas id="mainCanvas"></canvas>
|
|
<canvas id="arrowCanvas"></canvas>
|
|
<script type="module">
|
|
// Direct from mrdoob: https://www.jsdelivr.com/package/npm/three
|
|
import * as THREE from 'https://cdn.jsdelivr.net/npm/three@0.125.0/build/three.module.js';
|
|
import { OrbitControls } from 'https://cdn.jsdelivr.net/npm/three@0.125.0/examples/jsm/controls/OrbitControls.js';
|
|
import { GUI } from 'https://cdn.jsdelivr.net/npm/three@0.125.0/examples/jsm/libs/dat.gui.module.js';
|
|
import { Line2 } from 'https://cdn.jsdelivr.net/npm/three@0.125.0/examples/jsm/lines/Line2.js';
|
|
import { LineMaterial } from 'https://cdn.jsdelivr.net/npm/three@0.125.0/examples/jsm/lines/LineMaterial.js';
|
|
import { LineGeometry } from 'https://cdn.jsdelivr.net/npm/three@0.125.0/examples/jsm/lines/LineGeometry.js';
|
|
import { EdgeSplitModifier } from 'https://cdn.jsdelivr.net/npm/three@0.125.0/examples/jsm/modifiers/EdgeSplitModifier.js';
|
|
|
|
const data = {"camera":{"type":"Orthographic","focalDistance":100.0,"position_x":-5.851371765136719,"position_y":224.34979248046875,"position_z":101.94686889648438},"file":{},"objects":[{"name":"Body","color":"#727980","opacity":1.0,"verts":"2yfzf Z Y b Z Y b cyfzf cAfBf ZAfBf cCfDf ZCfDf cEfFf ZEfFf cGfHf ZGfHf cIfJf ZIfJf cKfJf ZKfJf cLfHf ZLfHf cMfFf ZMfFf cNfDf ZNfDf cOfBf ZOfBf cPfzf ZPfzf c a b Z a b c Y 1 Z Y 1 c 2 b 3QfRf YSfTf 3UfVf 3WfXf 3YfZf Y1f2f 33f4f 35f6f Y7f8f 39f6f Y0f8f 3!f4f 3#fZf Y$f2f 3%fXf 3&fRf Ylb b 3(fVf 3)fTf 3nbmb Z*f+f Z-f:f Z;f/f Z=f>f Z?f@f Z[f]f Z a@b Z[f^f Z=f_f Z*f,f Z.f@f Z bmb Z Y@b Z{f/f Z|f:f Z}f+f Z~f>f Z`f]f Z`f^f Z~f_f Z}f,f ZNfag Z-fbg ZMfcg Z;fdg ZOfeg ZLffg Z?fgg ZKfhg Z aFb ZIfhg Z YFb Z.fgg ZGffg Z{fdg ZEfcg Z|fbg ZPfig Znbob Z a 1 ZCfag ZAfeg Z bob Zyfig Z a 1 c 2 1 3Pfig cOfeg cNfag cMfcg cLffg cKfhg cIfhg cGffg cEfcg cCfag cAfeg cyfig cicjc 3gchc 3ichc 3jgkg 3lgmg 3ngog 3pgqg 3rgsg 3tgug 3 aoc 3tgvg 3pgwg 3jgxg 3 Yoc 3kcjc 3ygsg 3zgog 3Agmg 3Bgkg 3lchc 3kchc 3Cgvg 3Cgug 3Dgqg 3Dgwg 3Bgxg 3%fEg 3lgFg 3$fGg 3ngHg 3(fIg 3!fJg 3rgKg 30fLg 3 a0c 37fLg 3 Y0c 3ygKg 3gcfc 3)fMg 3lb 1 33fJg 3zgHg 31fGg 3AgFg 3WfEg 3UfIg 3ecfc 3 Znc 3ecnc 3mcfc 3mcnc 3lcfc 3SfMg 3 bmb 1nbmb 1 bob 1NgobOgPgobQgRgobSgTgobUgVgobWgXgobYgedob?cVgobZgnbob 1Rgob1gPgob2gNgob3gTgob4gXgob5g 3ob?c6gobZg6gobWg7gobSg8gobQg9gobOg0gobUg!gobYg!gob5g0gob4g9gob3g8gob2g7gob1g aFb c?fgg c;fdg c-fbg c*f,f c=f_f c[f^f c[f]f c=f>f c*f+f c-f:f c;f/f c?f@f c a@b c Y@b c.f@f c{f/f c|f:f c}f+f c~f>f c`f]f c`f^f c~f_f c}f,f c|fbg c{fdg c.fgg c YFb c&f#g Y#f$g Y9f%g Y5f%g YYf$g YQf#g Ygcfc(d)dfc(dgcfc&d)dfc 1ecfc&d*dfc(d*dfc 1ecnc(decnc&d&gnc(g)gnc*g+gnc-g:gnc;g/gnc=g>gnc?gmcnc(dednc?c@gnc[g]gnc^g_gnc,g&fnc.g{gnc|g}gnc~g#fnc`gahncbhchncdhehncfh9fncghhhncihjhnckhlhncmhnhncohphncqhrhncshthncuhmcfc&dmcnc&d>efc 1>efc(d?efc(dlcfc&d?efc 1lcfc(dlchc(dkchc(dkchc@ekcjc@evhjcwhxhjcyhzhjcAhBhjcChDhjcEhFhjcGh[ejc IDhjcHhzhjcIhJhjcGhicjc@eKhjcChLhjcyhMhjcwhNhjcAhOhjcEhmfjc IOhjcHhNhjcIhBhjcPhFhjcQhxhjcRhvhjcShMhjcShLhjcRhKhjcPhJhjcQhichc(dichc@egchc(dThUh YVhWh YXhYh YXhZh YVh1h YTh2h Y3h2h Y4h1h Y5hZh Y5hYh Y4hWh Y3hUh Yxfwf 1(dwf 1)dvf 1xfvf 1*dvf 1?evf 1>evf 1(dvf 1gcnc(dgcnc&d)dnc(d)dvf(d*dnc(d*dvf(d>evf(d>enc(dlcnc(d?enc(dlcnc&d?evf(dxfwf(d(dwf(dxfvf(d(dvf(dMhhcwhLhhcyhNhhcAhKhhcChOhhcEhJhhcGhmfhc IFhhcGhOhhcHhNhhcIhMhhcShLhhcRhKhhcPhzhhcAhvhhcwhxhhcyhBhhcChDhhcEhJhhcQh[ehc IFhhcQhBhhcPhxhhcRhvhhcShzhhcIhDhhcHh","facets":"2 a b c a c d e d f e a d g f h g e f i h j i g h k j l k i j m l n m k l o n p o m n q p r q o p s r t s q r u t v u s t w v x w u v y x z y w x A z B A y z b C c c C D E F c c F d d F f f F h G F E H F G I F H h J j j J l K J I L J K F J h I J F l M n N M L L M J J M l n O p p O r P O N Q O P M O n N O M r R t t R v S R Q T R S O R r Q R O v U x x U z z U B B U V W U T X U W V U X R U v T U R Y Z 1 1 2 Y Y 3 Z 2 4 Y Y 5 3 4 6 Y Y 7 5 Y 8 7 Y 9 8 0 ! # $ ! 0 % ! $ & ! % ( ! & ) ! ( * ! ) + ! * - ! + # ! 6 6 ! Y 9 : ; ; / = : / ; 9 > : Y > 9 / ? = = ? @ ? [ @ @ [ ] ] ^ _ _ ^ , [ ^ ] ^ . , , . { . | { { | } ~ `ab > ` ~ Y ` > }bb - |bb }bbcb - -cb ! ` Aab ` y A ` w y ` u w ` s u ` q s ` o q ` m o bdb Cebdbcb Cdbeb adb b edb a gdb e idb g kdb i mdb kcbdb ! `db mab A Bab Bfbgb E D D E c ~abfb ~fbhb >hbib > ~hb :ibjb : >ib /jbkb / :jb ?kblb ? /kb [lbmb [ ?lb ^mbnb ^ [mb .nbob . ^nb |obpb | .obbbpbqbbb |pbcbqbrbcbbbqbebrbsbebcbrb Csb D CebsbtbubvbwbubtbwbtbxbybxbtbzbubwbAbybtbBbubzbCbAbtbDbubBbEbubDbFbubEbGbtbHbGbCbtbIbGbHbJbIbHbKbJbHbLbKbHbMbLbHbMbHbNbMbObPbMbPbQbMbQbLbRbObMbSbRbMbTbFbUbVbTbUbVbUbWbXbFbTbXbubFbYbVbWbYbWbZb1bYbZb1bZb2b3b2b4b3b4b5b3b1b2b6b7b8b6bXb7b6bubXb9b5b0b9b3b5b!b9b0b!b0b#b V6b8b$b!b#b$b#bSb X6b V%b$bSb%bSbMb&b T S&b W T&b X W&b6b X Q&b S(b N L(b P N(b Q P(b)b&b(b&b Q*b H G*b I H*b K I*b L K*b(b L+b(b*b-b G E-b*b G-b Egb-b:b%b-bgb:b-b%bMb V8b B B8bfb !;b/b !/b Y;b !db;bdb=b>b `?b@b?b `[b `>b]b@b `^b `[b_b]b `,b `.b,b_b `{b,b.b|b{b.b}b|b.b~b}b.b`b~b.bdbacbcdbccacdbdcccdbecdcdbfcecdbgcfcdbhcgc=bic`b=bjcic=bkcjc=blckc=bmclc=bbcmc=bdbbc=b`b.bdb `^bdb^bhc Y.b ` Y/b.b @ ]nc @ncoc =ocpc = @oc ;pcqc ; =pc 9qcrc 9 ;qc 8rcsc 8 9rc 7sctc 7 8sc 5tcuc 5 7tc 3ucvc 3 5uc Zvcwc Z 3vc 1wcxc 1 Zwc 2xcyc 2 1xc 4yczc 4 2yc 6zcAc 6 4zc # 6Ac #AcBc 0 #Bc 0BcCc $CcDc $ 0Cc %DcEc % $Dc &EcFc & %Ec (FcGc ( &Fc )GcHc ) (Gc *HcIc * )Hc +IcJc + *Ic -JcKc - +Jc }KcLc } -Kc {LcMc { }Lc ,McNc , {Mc _NcOc _ ,Nc ] _ncnc _Oc8bPcfbfbPchbhbPcibibPcjb7bPc8bXbPc7bTbPcXbjbQckbkbQclbVbQcTbYbQcVbPcQcjbTbQcPclbRcmbmbRcnb1bRcYb3bRc1bYbRcQcQcRclbnbScob9bSc3b3bScRcRcScnbobTcpbpbTcqb!bTc9b$bTc!bScTcob9bTcScqbUcrbrbUcsbsbUc D DUcgb%bUc$b:bUc%bgbUc:bTcUcqb$bUcTcVcWcXcVcYcWcWcZcXcWc1cZc1c2cZcVc6bYcYc&b2c6b&bYc2c&bZcZc3c4cZc&b3c&b)b3c5c3c6c7c3c)b7c6c3c8c3c5c9c7c)b0c!c3c0c3c8c#c9c)b$c!c0c%c#c)b&c!c$c(c%c)b)c!c&c*c(c)b+c*c)b-c!c)c:c+c)b;c!c-c/c:c)b+b;c=c+b=c>c+b>c?c+b?c@c+b@c[c+b[c]c+b]c^c+b!c;c(b+b^c)b(b/c!c_c,c+b*b!c!c*b_c_c.c{c_c|c}c{c|c_c~c`c|c|c`c}c_c*b.c.c-b~c*b-b.c~c-b`cMbad`cMb`c-badMbNbadNbbdcdbdddNbHbbdbdHbddedfdddddgdedfdhdddddidgdhdjdddddkdidddldkdddmdldndodjdpdodndqdodpdrdodqdsdodrdjdodddsdtdodtdudodudvdodvdwdodxdHbydzdHbxdAdHbzdmdHbAdddHbmdBdtbwdCdtbBdDdtbCdEdtbDdydtbEdHbtbydwdtbododFdGdodtbFdtbvbFdFdvbubFdubHdHdub6bHd6bVc4b2bnc4bncOcIdCbAcIdAczcIdzcycIdycxcIdAbCbIdybAbIdxbybJdxcwcJdwcvcJdwbxbJdzbwbJdIdxcJdxbIdKdvcucKdBbzbKdzbJdKdJdvcLductcLdtcscLdDbBbLdEbDbLdKducLdBbKdMdscrcMdrcqcMdFbEbMdUbFbMdLdscMdEbLdNdqcpcNdpcocNdocncNdnc2bNdWbUbNdZbWbNd2bZbNdMdqcNdUbMdOd4bOcOdOcNcOdNcMcOdMcLcOd5b4bOd0b5bOd#b0bPdLcKcPdKcJcPdSb#bPdRbSbPdOdLcPd#bOdQdJcIcQdObRbQdRbPdQdPdJcRdIcHcRdHcGcRdPbObRdQbPbRdQdIcRdObQdSdGcFcSdFcEcSdLbQbSdKbLbSdRdGcSdQbRdTdEcDcTdDcCcTdCcBcTdBcGbTdJbKbTdIbJbTdGbIbTdSdEcTdKbSdCbGbBcCbBcAc;bUd/bVdUd;bWd.bXdYdWdYcYdYc2cYd.bWdZd1d.cZd.c~c=bZd2d=b1dZd=b.bYd=bYd1dUdXd.bUd.b/b2dVd;b2d;b=b_b,b#c#c,b9c9c{b7c,b{b9c7c|b6c{b|b7c6c}b5c|b}b6c5c~b8c}b~b5c8c`b0c~b`b8c0cic$c`bic0c$cjc&cicjc$c&ckc)cjckc&c)clc-ckclc)clcmc-c-cmc;cmcbc;c;cbc=cbcac=c=cac>caccc>c>ccc?cccdc?c?cdc@cdcec@c@cec[cecfc[c[cfc]cfcgc]c]cgc^cgchc^c^chc(bhc^b(b(b^b/c^b[b/c/c[b:c[b>b:c:c>b+c+c?b*c>b?b+c?b@b*c*c@b(c(c]b%c@b]b(c%c_b#c]b_b%c3dVc4d4dVcXcZc4dXc4c4dZcWd5dYcYc5dWcWd6d5d5d1cWc5d7d1c1c7d2c8dYd7d7dYd2c3c7d4c4c5d4d7d5d4c5d3d4d3c8d7d9d!c0d9d8d3c9d3c!c!d#d$d#d,c$d#d0d,c0d!c,c}c,c_c$d,c}c`c!d}c}c!d$d1d0d.c.c0d{c1d9d0d0d#d{c{c#d|c|c#d~c%dZd#d#dZd~cFdHd&d(dbdFd(dFd&dadbd(dVc&dHd3d)dVc6d3d5d6d)d3d`cad(d!d%d#d*d!d`c*d%d!d)d&dVc(d*d`c+d-dGdGd:d+d-d;dGdGd/d:d;d=dGdGd>d/d=d?dGd>dFd@dGdFd>dFd[d@dFd]d[dFd^d]dFd_d^d,dcd.d.dcd{d{dcd|d|dcd?d?dcdGd,d}dcdFd~d_d}d`dcdFdae~daebdbebebdcecebddedebdeeeebdfefebd`d`dbdcdFdbdaecdodGdddodcd`d}dkdkd}didid,dgd}d,didgd.deded.dfd,d.dgd.d{dfdfd|dhd{d|dfdhd?djdjd?dnd|d?dhd?d=dndnd;dpd=d;dndpd-dqd;d-dpdqd+drdrd+dsd-d+dqd+d:dsdsd:dtd:d/dtd/d>dtdtd>dud>d@dudud@dvd@d[dvdvd[dwdwd[dBd[d]dBd]d^dBdBd^dCdCd^dDd^d_dDd_d~dDdDd~dEdEd~dyd~daeydaebeydydbexdbecexdxdcezdcedezdzddeAdAdeemddeeeAdmdfeldeefemdfe`dldld`dkdUdVd&d&dVd(dXd&d)dXdUd&dWdXd6d6dXd)d1dYd9d9dYd8d2dZd*d*dZd%dVd*d(dVd2d*d","wires":["1abcdecfgchicjkclmcnocpqcrsctucvwcxyczAcBCcDEcFGcHIcJGcKEcLCcMAcNycOwcPucQscRqcSocTmcUkcVicWgcXecYbcYbZXeZWgZViZUkZTmZSoZRqZQsZPuZOwZNyZMAZLCZKEZJGZHIZFGZDEZBCZzAZxyZvwZtuZrsZpqZnoZlmZjkZhiZfgZdeZabZabc","1Y1cYbcYbZY1ZY1c","2 a b c d e c f g c h i c j k c l m c n o c p q c r s c t u c v w c x y c z A c B C c D E c F G c H I c J G c K E c L C c M A c N y c O w c P u c Q s c R q c S o c T m c U k c V i c W g c X e c Y b c 2 b 3 4 5 3 6 7 3 8 9 3 0 ! 3 # $ 3 % & 3 ( ) 3 * + 3 - : 3 ; / 3 = > 3 ? @ 3 [ ] 3 ^ _ 3 , . 3 H { 3 | . 3 } _ 3 ~ ] 3 ` @ 3ab > 3bb / 3cb : 3db + 3eb ) 3fb & 3gb $ 3hb ! 3ib 9 3jb 7 3kb 5 3lb b 3 a b c","2 bmb Znbmb Znbob Z bob Z bmb Z","2 Y b Z X e Z W g Z V i Z U k Z T m Z S o Z R q Z Q s Z P u Z O w Z N y Z M A Z L C Z K E Z J G Z H I Z F G Z D E Z B C Z z A Z x y Z v w Z t u Z r s Z p q Z n o Z l m Z j k Z h i Z f g Z d e Z a b Z a 1 Z dpb Z fqb Z hrb Z jsb Z ltb Z nub Z pvb Z rwb Z txb Z vyb Z xzb Z zAb Z BBb Z DCb Z FDb Z ZEb Z JDb Z KCb Z LBb Z MAb Z Nzb Z Oyb Z Pxb Z Qwb Z Rvb Z Sub Z Ttb Z Usb Z Vrb Z Wqb Z Xpb Z Y 1 Z Y b Z","2 aFb ZGbHb ZIbJb ZKbLb ZMbNb ZObPb ZQbRb ZSbTb ZUbVb ZWbXb ZYbZb Z1b2b Z3b4b Z5b6b Z7b8b Z9b0b Zlb!b Z9b#b Z7b$b Z5b%b Z3b&b Z1b(b ZYb)b ZWb*b ZUb+b ZSb-b ZQb:b ZOb;b ZMb/b ZKb=b ZIb>b ZGb?b Z a@b Z Y@b Z[b?b Z]b>b Z^b=b Z_b/b Z,b;b Z.b:b Z{b-b Z|b+b Z}b*b Z~b)b Z`b(b Zac&b Zbc%b Zcc$b Zdc#b Z 2!b Zdc0b Zcc8b Zbc6b Zac4b Z`b2b Z~bZb Z}bXb Z|bVb Z{bTb Z.bRb Z,bPb Z_bNb Z^bLb Z]bJb Z[bHb Z YFb Z aFb Z","1a1cabcabZa1Za1c","1YbcY1c2132b3Ybc","2 Y 1 c Xpb c Wqb c Vrb c Usb c Ttb c Sub c Rvb c Qwb c Pxb c Oyb c Nzb c MAb c LBb c KCb c JDb c ZEb c FDb c DCb c BBb c zAb c xzb c vyb c txb c rwb c pvb c nub c ltb c jsb c hrb c fqb c dpb c a 1 c a 1 Z dpb Z fqb Z hrb Z jsb Z ltb Z nub Z pvb Z rwb Z txb Z vyb Z xzb Z zAb Z BBb Z DCb Z FDb Z ZEb Z JDb Z KCb Z LBb Z MAb Z Nzb Z Oyb Z Pxb Z Qwb Z Rvb Z Sub Z Ttb Z Usb Z Vrb Z Wqb Z Xpb Z Y 1 Z Y 1 c","2ecfc 3gcfc 3gchc 3ichc 3icjc 3kcjc 3kchc 3lchc 3lcfc 3mcfc 3mcnc 3 Znc 3ecnc 3ecfc 3","2 aoc 3pcqc 3rcsc 3tcuc 3vcwc 3xcyc 3zcAc 3BcCc 3DcEc 3FcGc 3HcIc 3JcKc 3LcMc 3NcOc 3PcQc 3RcSc 3Tc!b 3RcUc 3PcVc 3NcWc 3LcXc 3JcYc 3HcZc 3Fc1c 3Dc2c 3Bc3c 3zc4c 3xc5c 3vc6c 3tc7c 3rc8c 3pc9c 3 a0c 3 Y0c 3!c9c 3#c8c 3$c7c 3%c6c 3&c5c 3(c4c 3)c3c 3*c2c 3+c1c 3-cZc 3:cYc 3;cXc 3/cWc 3=cVc 3>cUc 3?c!b 3>cSc 3=cQc 3/cOc 3;cMc 3:cKc 3-cIc 3+cGc 3*cEc 3)cCc 3(cAc 3&cyc 3%cwc 3$cuc 3#csc 3!cqc 3 Yoc 3 aoc 3","2 2 b 3 2 1 3 4@c 3 6[c 3 8]c 3 0^c 3 #_c 3 %,c 3 (.c 3 *{c 3 -|c 3 ;}c 3 =~c 3 ?`c 3 [ad 3 ^bd 3 ,cd 3 Zdd 3 |cd 3 }bd 3 ~ad 3 ``c 3ab~c 3bb}c 3cb|c 3db{c 3eb.c 3fb,c 3gb_c 3hb^c 3ib]c 3jb[c 3kb@c 3lb 1 3lb b 3kb 5 3jb 7 3ib 9 3hb ! 3gb $ 3fb & 3eb ) 3db + 3cb : 3bb / 3ab > 3 ` @ 3 ~ ] 3 } _ 3 | . 3 H { 3 , . 3 ^ _ 3 [ ] 3 ? @ 3 = > 3 ; / 3 - : 3 * + 3 ( ) 3 % & 3 # $ 3 0 ! 3 8 9 3 6 7 3 4 5 3 2 b 3","2 a 1 c a b clb b 3lb 1 3 a 1 c","2nbmb Z bmb Z bmb 1nbmb 1nbmb Z","2 bob 1 bob Z bmb Z bmb 1 bob 1","2nbob Z bob Z bob 1nbob 1nbob Z","2edob?cfdobgdhdobidjdobkdldobmdndobodpdobqdrdobsdtdobudvdobwdxdobydzdobAdBdobCdDdobEdFdobGdHdobId Zob 3JdobIdKdobGdLdobEdMdobCdNdobAdOdobydPdobwdQdobudRdobsdSdobqdTdobodUdobmdVdobkdWdobidXdobgd 3ob?cXdobYdWdobZdVdob1dUdob2dTdob3dSdob4dRdob5dQdob6dPdob7dOdob8dNdob9dMdob0dLdob!dKdob#dJdob$d Hob%dHdob$dFdob#dDdob!dBdob0dzdob9dxdob8dvdob7dtdob6drdob5dpdob4dndob3dldob2djdob1dhdobZdfdobYdedob?c","2nbmb 1nbmb Znbob Znbob 1nbmb 1","2 aFb cGbHb cIbJb cKbLb cMbNb cObPb cQbRb cSbTb cUbVb cWbXb cYbZb c1b2b c3b4b c5b6b c7b8b c9b0b clb!b c9b#b c7b$b c5b%b c3b&b c1b(b cYb)b cWb*b cUb+b cSb-b cQb:b cOb;b cMb/b cKb=b cIb>b cGb?b c a@b c a@b ZGb?b ZIb>b ZKb=b ZMb/b ZOb;b ZQb:b ZSb-b ZUb+b ZWb*b ZYb)b Z1b(b Z3b&b Z5b%b Z7b$b Z9b#b Zlb!b Z9b0b Z7b8b Z5b6b Z3b4b Z1b2b ZYbZb ZWbXb ZUbVb ZSbTb ZQbRb ZObPb ZMbNb ZKbLb ZIbJb ZGbHb Z aFb Z aFb c","2 a@b c Y@b c Y@b Z a@b Z a@b c","2 Y@b c[b?b c]b>b c^b=b c_b/b c,b;b c.b:b c{b-b c|b+b c}b*b c~b)b c`b(b cac&b cbc%b ccc$b cdc#b c 2!b cdc0b ccc8b cbc6b cac4b c`b2b c~bZb c}bXb c|bVb c{bTb c.bRb c,bPb c_bNb c^bLb c]bJb c[bHb c YFb c YFb Z[bHb Z]bJb Z^bLb Z_bNb Z,bPb Z.bRb Z{bTb Z|bVb Z}bXb Z~bZb Z`b2b Zac4b Zbc6b Zcc8b Zdc0b Z 2!b Zdc#b Zcc$b Zbc%b Zac&b Z`b(b Z~b)b Z}b*b Z|b+b Z{b-b Z.b:b Z,b;b Z_b/b Z^b=b Z]b>b Z[b?b Z Y@b Z Y@b c","2 YFb c aFb c aFb Z YFb Z YFb c","2 Y 1 c Xpb c Wqb c Vrb c Usb c Ttb c Sub c Rvb c Qwb c Pxb c Oyb c Nzb c MAb c LBb c KCb c JDb c ZEb c FDb c DCb c BBb c zAb c xzb c vyb c txb c rwb c pvb c nub c ltb c jsb c hrb c fqb c dpb c a 1 clb 1 3kb@c 3jb[c 3ib]c 3hb^c 3gb_c 3fb,c 3eb.c 3db{c 3cb|c 3bb}c 3ab~c 3 ``c 3 ~ad 3 }bd 3 |cd 3 Zdd 3 ,cd 3 ^bd 3 [ad 3 ?`c 3 =~c 3 ;}c 3 -|c 3 *{c 3 (.c 3 %,c 3 #_c 3 0^c 3 8]c 3 6[c 3 4@c 3 2 1 3 Y 1 c","2ecfc&decfc 3gcfc 3gcfc(dgcfc&decfc&d","2)dfc(d*dfc(d*dfc 1)dfc 1)dfc(d","2ecnc&decnc(decnc 3ecfc 3ecfc&decnc&d","2 Znc 3mcnc 3mcnc(decnc(decnc 3 Znc 3","2ednc?c+dnc-d:dnc;d/dnc=djdnc1d>dnc?d@dnc[d]dnc^dpdnc4d_dnc,d.dnc{d|dnc}dvdnc7d~dnc`daencbecencdeBdnc0deencfegencheiencjeHdnc$dkenclemencneoencpeKdnc#dqencresencteuencveNdnc9dwencxeyenczeAencBeQdnc6dCencDeEencFeGencHeTdnc3dIencJeKencLeMencNeWdncZdOencPeQencReSencTeXdncgdUencVeWencXeYencZeUdncmd1enc2e3enc4e5enc6eRdncsd7enc8e9enc0e!enc#eOdncyd$enc%e&enc(e)enc*eLdncEd+enc-e:enc;e/enc=e Znc 3HdncIdFdncGdDdncEdBdncCdzdncAdxdncydvdncwdtdncudrdncsdpdncqdndncodldncmdjdnckdhdncidfdncgdednc?c","2mcfc&dmcfc 3mcnc 3mcnc(dmcnc&dmcfc&d","2lcfc(dlcfc 3mcfc 3mcfc&dlcfc&dlcfc(d","2>efc(d?efc(d?efc 1>efc 1>efc(d","2lcfc 3lchc 3lchc(dlcfc(dlcfc 3","2lchc 3kchc 3kchc(dlchc(dlchc 3","2kcjc@ekcjc 3kchc 3kchc(dkchc@ekcjc@e","2icjc 3kcjc 3kcjc@eicjc@eicjc 3","2[ejc I]ejc^e_ejc,e.ejc{e|ejc}e~ejc`eafjcbfcfjcdf Zjcefffjcdfgfjcbfhfjc`eifjc}ejfjc{ekfjc,elfjc^emfjc Ilfjcnfkfjcofjfjcpfifjcqfhfjcrfgfjcsfffjctf Hjcufcfjctfafjcsf~ejcrf|ejcqf.ejcpf_ejcof]ejcnf[ejc I","2ichc@eichc(dichc 3icjc 3icjc@eichc@e","2ichc 3gchc 3gchc(dichc(dichc 3","2gchc 3gcfc 3gcfc(dgchc(dgchc 3","2 YFb c aFb c a0c 3 Y0c 3 YFb c","2 aFb cGbHb cIbJb cKbLb cMbNb cObPb cQbRb cSbTb cUbVb cWbXb cYbZb c1b2b c3b4b c5b6b c7b8b c9b0b clb!b c9b#b c7b$b c5b%b c3b&b c1b(b cYb)b cWb*b cUb+b cSb-b cQb:b cOb;b cMb/b cKb=b cIb>b cGb?b c a@b c aoc 3pcqc 3rcsc 3tcuc 3vcwc 3xcyc 3zcAc 3BcCc 3DcEc 3FcGc 3HcIc 3JcKc 3LcMc 3NcOc 3PcQc 3RcSc 3Tc!b 3RcUc 3PcVc 3NcWc 3LcXc 3JcYc 3HcZc 3Fc1c 3Dc2c 3Bc3c 3zc4c 3xc5c 3vc6c 3tc7c 3rc8c 3pc9c 3 a0c 3 aFb c","2 Y@b c[b?b c]b>b c^b=b c_b/b c,b;b c.b:b c{b-b c|b+b c}b*b c~b)b c`b(b cac&b cbc%b ccc$b cdc#b c 2!b cdc0b ccc8b cbc6b cac4b c`b2b c~bZb c}bXb c|bVb c{bTb c.bRb c,bPb c_bNb c^bLb c]bJb c[bHb c YFb c Y0c 3!c9c 3#c8c 3$c7c 3%c6c 3&c5c 3(c4c 3)c3c 3*c2c 3+c1c 3-cZc 3:cYc 3;cXc 3/cWc 3=cVc 3>cUc 3?c!b 3>cSc 3=cQc 3/cOc 3;cMc 3:cKc 3-cIc 3+cGc 3*cEc 3)cCc 3(cAc 3&cyc 3%cwc 3$cuc 3#csc 3!cqc 3 Yoc 3 Y@b c","2 a@b c Y@b c Yoc 3 aoc 3 a@b c","2nbmb 1 bmb 1 bob 1nbob 1nbmb 1","2?evf 1(dvf 1(dwf 1xfwf 1xfvf 1)dvf 1)dfc 1*dfc 1*dvf 1>evf 1>efc 1?efc 1?evf 1","2edob?cfdobYdhdobZdjdob1dldob2dndob3dpdob4drdob5dtdob6dvdob7dxdob8dzdob9dBdob0dDdob!dFdob#dHdob$d Hob%dJdob$dKdob#dLdob!dMdob0dNdob9dOdob8dPdob7dQdob6dRdob5dSdob4dTdob3dUdob2dVdob1dWdobZdXdobYd 3ob?cXdobgdWdobidVdobkdUdobmdTdobodSdobqdRdobsdQdobudPdobwdOdobydNdobAdMdobCdLdobEdKdobGdJdobId Zob 3HdobIdFdobGdDdobEdBdobCdzdobAdxdobydvdobwdtdobudrdobsdpdobqdndobodldobmdjdobkdhdobidfdobgdedob?cednc?cfdncgdhdncidjdnckdldncmdndncodpdncqdrdncsdtdncudvdncwdxdncydzdncAdBdncCdDdncEdFdncGdHdncId Znc 3/enc=e:enc;e+enc-eLdncEd)enc*e&enc(e$enc%eOdncyd!enc#e9enc0e7enc8eRdncsd5enc6e3enc4e1enc2eUdncmdYencZeWencXeUencVeXdncgdSencTeQencReOencPeWdncZdMencNeKencLeIencJeTdnc3dGencHeEencFeCencDeQdnc6dAencBeyenczewencxeNdnc9duencvesencteqencreKdnc#doencpemencnekencleHdnc$diencjegencheeencfeBdnc0dcencdeaencbe~dnc`dvdnc7d|dnc}d.dnc{d_dnc,dpdnc4d]dnc^d@dnc[d>dnc?djdnc1d/dnc=d:dnc;d+dnc-dednc?c","2gcnc(dgcfc(dgcfc&dgcnc&dgcnc(d","2gcnc&dgcfc&decfc&decnc&dgcnc&d","2)dvf 1)dvf(d)dnc(d)dfc(d)dfc 1)dvf 1","2*dnc(d*dfc(d)dfc(d)dnc(d*dnc(d","2*dnc(d*dvf(d*dvf 1*dfc 1*dfc(d*dnc(d","2ecnc(d*dnc(d)dnc(dgcnc(dgcnc&decnc&decnc(d","2*dnc(decnc(dmcnc(d>enc(d>evf(d*dvf(d*dnc(d","2lcnc(d?enc(d>enc(dmcnc(dmcnc&dlcnc&dlcnc(d","2mcnc&dmcfc&dlcfc&dlcnc&dmcnc&d","2lcfc(dlcnc(dlcnc&dlcfc&dlcfc(d","2>evf 1>evf(d>enc(d>efc(d>efc 1>evf 1","2?enc(d?efc(d>efc(d>enc(d?enc(d","2?enc(d?evf(d?evf 1?efc 1?efc(d?enc(d","2(dwf(d(dvf(d?evf(d?enc(dlcnc(dlcfc(dlchc(dkchc(dichc(dgchc(dgcfc(dgcnc(d)dnc(d)dvf(dxfvf(dxfwf(d(dwf(d","2ichc@eichc(dkchc(dkchc@eichc@e","2[ehc I]ehcnf_ehcof.ehcpf|ehcqf~ehcrfafhcsfcfhctf Hhcufffhctfgfhcsfhfhcrfifhcqfjfhcpfkfhcoflfhcnfmfhc Ilfhc^ekfhc,ejfhc{eifhc}ehfhc`egfhcbfffhcdf Zhcefcfhcdfafhcbf~ehc`e|ehc}e.ehc{e_ehc,e]ehc^e[ehc I","2icjc@eichc@ekchc@ekcjc@eicjc@e","2[ehc I]ehc^e_ehc,e.ehc{e|ehc}e~ehc`eafhcbfcfhcdf Zhcefffhcdfgfhcbfhfhc`eifhc}ejfhc{ekfhc,elfhc^emfhc Ilfhcnfkfhcofjfhcpfifhcqfhfhcrfgfhcsfffhctf Hhcufcfhctfafhcsf~ehcrf|ehcqf.ehcpf_ehcof]ehcnf[ehc I[ejc I]ejcnf_ejcof.ejcpf|ejcqf~ejcrfafjcsfcfjctf Hjcufffjctfgfjcsfhfjcrfifjcqfjfjcpfkfjcoflfjcnfmfjc Ilfjc^ekfjc,ejfjc{eifjc}ehfjc`egfjcbfffjcdf Zjcefcfjcdfafjcbf~ejc`e|ejc}e.ejc{e_ejc,e]ejc^e[ejc I","2(dwf(d(dwf 1xfwf 1xfwf(d(dwf(d","2xfwf(dxfwf 1xfvf 1xfvf(dxfwf(d","2)dvf 1xfvf 1xfvf(d)dvf(d)dvf 1","2>evf 1*dvf 1*dvf(d>evf(d>evf 1","2(dvf 1?evf 1?evf(d(dvf(d(dvf 1","2(dvf(d(dvf 1(dwf 1(dwf(d(dvf(d"],"faceColors":[],"facesToFacets":["1abcdefghijklmnopqrstuvwxyz","1AB","1CDEFGHIJKLMNOPQRSTUVWXYZ1234567890!#$%","2 & ( ) * + - : ; / = > ? @ [ ] ^ _ , . { | } ~ `abbbcbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzbAbBbCbDbEbFbGbHbIbJbKbLb","2MbNb","2ObPb","2QbRbSbTbUbVbWbXbYbZb1b2b3b4b5b6b7b8b9b0b!b#b$b%b&b(b","2)b*b+b-b:b;b/b=b>b?b@b[b]b^b_b,b.b{b|b}b~b`bacbcccdcecfcgchcicjckclcmcncocpcqcrcsctcucvcwcxcyczcAcBcCcDcEcFcGcHcIcJcKcLcMcNcOcPcQcRcScTcUcVcWc","2XcYc","2Zc1c","22c3c","24c5c6c7c8c9c0c!c#c$c%c&c(c)c*c+c-c:c;c/c=c>c?c@c[c]c^c_c,c.c","2{c|c","2}c~c`cadbdcdddedfdgdhdidjdkdldmdndodpdqdrdsdtdudvdwd","2xdyd","2zdAdBdCdDdEdFdGdHdIdJdKdLdMdNdOdPdQdRdSdTdUdVdWdXdYd","2Zd1d","22d3d4d5d6d7d8d9d0d!d#d$d%d&d(d)d*d+d-d:d;d/d=d>d?d@d[d]d^d_d,d.d{d|d}d~d`dae","2becedeeefegeheieje","2keleme","2neoepeqereseteuevewexeyezeAeBeCeDeEeFeGeHeIeJeKeLeMeNeOePe","2QeReSe","2TeUeVeWeXeYeZe1e2e","23e4e","25e6e","27e8e9e","20e!e#e$e%e&e(e)e*e+e-e:e;e/e=e>e?e@e[e]e^e_e,e.e{e|e}e~e`eaf","2bfcfdf","2efff","2gfhf","2ifjf","2kflfmfnfofpfqfrfsftfufvfwfxfyfzfAfBfCfDfEfFfGfHfIfJfKfLfMfNfOfPfQfRfSfTfUfVf","2WfXfYfZf1f2f3f4f5f6f7f8f9f0f!f#f$f%f&f(f)f*f+f-f:f;f/f=f>f?f@f[f]f^f_f,f.f{f","2|f}f","2~f`fagbgcgdgegfggghgigjgkglgmgng","2ogpgqgrgsgtgugvgwgxgygzgAgBgCgDgEgFgGgHgIgJgKgLgMgNgOgPgQgRgSgTgUgVgWgXgYgZg1g2g3g4g5g6g7g8g9g0g!g#g$g%g","2&g(g","2)g*g","2+g-g:g","2;g/g","2=g>g?g","2@g[g]g^g","2_g,g.g{g","2|g}g~g`g","2ahbh","2chdh","2ehfhgh","2hhih","2jhkhlh","2mhnhohphqhrhshthuhvhwhxhyhzh","2AhBhChDhEhFhGhHhIhJhKhLhMhNhOhPhQhRhShThUhVhWhXhYhZh1h2h3h4h","25h6h","27h8h9h0h!h#h$h%h&h(h)h*h+h-h:h;h/h=h>h?h@h[h]h^h_h,h.h{h|h}h~h`haibicidieifigihiiijikiliminioipiqirisiti","2uivi","2wixi","2yizi","2AiBi","2CiDi","2EiFi"],"floats":"SK=p)$gu-[Tv94UvcvtnB1#JC2;Nv!{EGH}Th`QBpz(cgvg7qL?ZiEim02Fp{EYUCx9Mf#K$.VKO[uqbXIMw@?4%+[Gk%fuK[*ZAT&J@+&]5U%oBl/+c4Bc,ddDXu]hmN)=o>CM&rr8-jK$7T-$ON9mb#CjGaK}0Phojv1vKI_A&U_Tva0UvPhojOc7uwO2b%iE&}7/S9A9M3}!C=:nM+[GkF(DB$UjcL_TU?_K>+u8-4LaED?t.cvtn^:&Hk,Zb[;euM{Tvt5Uvd0Uv)+pnXk$J_#O&].fEKe5/uz:=`[LO!xldF:CH~Hrz8]1MBy0m3{ROIx/cuJ%MC/^Il]N5%]OmDb(KKS,N%lh7ZTm_._?Jc)%20jkc689[A}x-M/8P0j#lZhqBe_?ciM:+o2yx;Kw_F0okGvxLwLZUB>Tv)leU0?-u3nqb)jfE{;8PBy0mI`=ppH,:@d0+9,/=4rJOUvxb^;N@u]1M0j#ljQHq%=08uTdQjSs{D4~U{rBb6>Tv~@yi2v,V5YeuL=uUMd50x]a(jaNcHO8!UDy7&m!mTVmFhr9AMbW7a9!Yu|F9+QJc[MhFG{%4H&3i86kF!~^N%5y.`9Uv3{sK}m|t{=F0IOBi6K>pUP,:({4TVJml4Tcq#OdDw#,#36LO!=tb.&D:2XVK.VKO||ubzDi);:nMpnuKL6,:-8eX5-nlK!GqDesH^R}Pt:Zl89cq}mM?+=i#/kaVtFxb7u:+D-PKT-$OSvvb1^w,B?t..M&K|usHTeCx{2GmhXjqO[JvsF|G#5vLDT,:}G#/C.]28UvbXx9M9W/imlnqH(D:J-9WQ_(L{jsH7V5!P:U9,+vbZr8-d{^msynq`xg7ZB#{UBwMk$fuNl.w|{DwGH#/Q4l-,ucn.VKO@kkd;K![XVoJZJC0qW5!t?f-g(VmT-$O$amd]$O&/M/Sd0UvD4~U4ftb=>Tv~@yiTN@qGS|tG5Uvgr53Ili]PyocSfLv04UvIli]n%tbu)|yjSs{j9dI@DwbuTdQESvFlH+Mo=08hBrz0j#lf2HqpQX[v]1MGsIjiruq+=N@x%oT9#S;z@AbbbLX9,/=da62]ewbBe0+4c5QK]wMJq.:l.x-By0mg(Ir.jsH`;8PI!OmqjnqCrfEVXEK&H^2zMEb=X+*)leU(Lu9*vvbQAY:/7Gj.8cq$lM?*&LZUwtKvl1A&c$-k2_HB!ub65{E,U[mc1{pdPdD~d8ZTisJ*L,:O(})f{mB0LubX:F_~@yiQ@[Ha94cv7/8)mgEbIrz?jx?|_?J08|kWWBV!Ble%,8U|FP@;ey-Ek?,xz:=8C{m~M~2~{5eEYeu[EY:`)6J~4X9aVJcZptM_wEEeRmm2.4ET$^N79{EAeL^{(vVIQCcqa+PZw&xc`fj}djEUn*8Y>F_`9Uvk$GOB>tbomg69WJ8fnv,P`Gkb%?p2hMHm}YEcc5)Toj?hp`H_:5d4e1AYNgYmEbR]G@jQrbVH9tb#yAQ)B>?K;u^UC7l!DFp@s%A~^h8I>vE%BiS)0pBCdkdKc08jy+zb9KN{0nluuIO37mbzg|xLPuY7Oo,Zt^7|N5m)0pB#@ic5K4[CTx)X8rQ/IJJ@bwQAm0khp`HB74c(PXUPUJ=!IUKTcw?YvTN{0nl2WY9WonfF^G2}L5Gk/=$a>jx26sD]G@jF?7;$F9fHKc9Fbr#X-8zvloX`2J{~@yi3{sK.pguX>Tv~@yiZlRpY@Jv8!{^&}-JHDkInl7e{yX[X9K)k1B7*>tm#L@&S-tbn|Z.AHhM_2)0ou4k$F>pii(Ue=#%l0jKELswz4)H?e8frSNUf)}*n>,4F+~k7;0;nrqba~_=R0M.6+q,b~VmM0=o/5+UfI~zY?SD,>@,[}Uu>y0f$7~t)e9I(Vk#Q+]$;U|.x4Fm}]:;W+hc*C7&fy8!jQx7c[xXE/M0B>{k0^LB{%8f9nyHCMX4:LBFe)4)ulc.n:Ek{Cn(Jc4cp>~8%z1{KM,L^.|EsH0=3WCi#RhIzl8e#RXUH8.XvR`)n:B.IoPE|4vnw,|2}n5czfeOQGWCM#?vSKJ)S~>>4@Si!faIdb6d+jL&N34Go`x-=>,4Y,LMLigj>z%O252cm1g;?NZE6sc7de-RFq^Z2rnkHquBXokdy@[N&=$%[;sC71B7u|qzOHQk)ZHO)T1bsD![,/XDc5Uv~@yi`m)Me+~t!gKvfGU6q{LE;H!l.T%K~73A)mm0t[OFI1o5(frVZd2boSe;N3MS1~e@;H!lr(Xu{G/c[+i8$l53uU|iJO/o#TZArT3$F1o5@*&ui&mbo-S^j2nSeIdm+uvK[te;ra!N,D/7:6WO0)+cOC]Vp.jzB[Wj;2tKZ@lu>>Tv~@yiG_^pKFKvb&UGff^Z+4km,3%JQ)l2YIMw|?^L+1^D2g4OC=2b4^!A}N5vD}VW]grnbG/oJ,A&7po_(1^D.2BBazqbf;S^NO>W*T}({CROH+ld2eM&jhk*c//WmZZmxglIDF/cNTQ1={;xF%5R-/ZjVGvL^.[Nsd_Wi?bW]z2$?~EVhq2bMfr2om#z?z2$jD0u2sqbQm^WL.d)-/Zj9d@p1)NU:%HNFz%5[o#ONBxb=@X.(1^D1|ROK+vb)t.(~JS_oU)L1D1A#M5v,u@lT7>prb/NfO>W]grnM2jq7(|y{](+[U%K;GX[K8j#gf^Z^a}m1|ROFold%!O&#K.w1;%5^W{$eCuKvTb;VdkW5fKjTX5Fz-n2xs|{o#SiE2kF^*b;h|,DyhLN-`wM&j!8l(mL3wun]VHqpQX[4;/W$?`lponqGo^Wv0z7.9:;mMEbePK>nMo1kq&K.(f2?Qt=ErcIro5cC-/8s,|@)?mL5K~^j,!.$?`lo64Eol1A{pNyR,?lcW{p=jjGg@XW=;Uj-P{p]*pKYlKN^9NiRQFp{M![@f{xX@(YZsew:O`HB_5db1;NG@RT7qU,^8^mD+#2b9mb7l70#zRGTSgG{OlUInsnJm7;{:9f`WK2MdYDba{M^6;6)[aLn@>l|3mB/?1bvr9A5xs>~)|#kV52!wqby;M>r{9ER1dJp^Ji-j?p]rTHKKK>9/.*YzB>C$iIK,7eTb08vD1-vMG[pR%1?Thn3]?p0Wn;jB,Xpz}3Ku!Z|)n&>GUmL13;P$tb3BR1.;),pJ?5Ju%S>GUmPIEpK*s2/o`z0]P*Bxiy9)zz?ThnYdL9pu5c>_z2{O#LLyi+~F8L~^q41Sckt8(H#Vldd{[N~OL>Fp`EYi4S._oA6Zfl9g&K]CQU^_{x9V-FNLP$-OkVc31b7+q2u3IDRP_MI.hj)OVuhH/cn&d50D|IG^VlR_;ox3{tIWJ8wuxXgA$u0%mbhI~zQP_Me49j13tKpCM&s(XMQ`$3{CROnfwb{%HN:65RU+)LFbC&4W@Zeh2l/hgq_=_X{KVD?>#J)I![M)0#2Xa6H5iL"}],"compressed":true,"base":"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!#$%&()*+-:;/=>?@[]^_,.{|}~`","baseFloat":",.-0123456789"};
|
|
|
|
// Z is up for FreeCAD
|
|
THREE.Object3D.DefaultUp = new THREE.Vector3(0, 0, 1);
|
|
|
|
const defaultWireColor = new THREE.Color('rgb(0,0,0)');
|
|
const defaultWireLineWidth = 2; // in pixels
|
|
|
|
const raycasterObj = []; // list of obj that can mouseover highlight
|
|
|
|
const canvas = document.querySelector('#mainCanvas');
|
|
|
|
const scene = new THREE.Scene();
|
|
|
|
const renderer = new THREE.WebGLRenderer({
|
|
alpha: true,
|
|
antialias: true,
|
|
canvas: canvas
|
|
}); // Clear bg so we can set it with css
|
|
renderer.setClearColor(0x000000, 0);
|
|
|
|
let renderRequested = false;
|
|
|
|
// HemisphereLight gives different colors of light from the top
|
|
// and bottom simulating reflected light from the 'ground' and
|
|
// 'sky'
|
|
scene.add(new THREE.HemisphereLight(0xC7E8FF, 0xFFE3B3, 0.4));
|
|
|
|
const dLight1 = new THREE.DirectionalLight(0xffffff, 0.4);
|
|
dLight1.position.set(5, -2, 3);
|
|
scene.add(dLight1);
|
|
const dLight2 = new THREE.DirectionalLight(0xffffff, 0.4);
|
|
dLight2.position.set(-5, 2, 3);
|
|
scene.add(dLight2);
|
|
|
|
if (data.compressed) {
|
|
const base = data.base;
|
|
const baseFloat = data.baseFloat;
|
|
|
|
function baseDecode(input) {
|
|
const baseCt = base.length;
|
|
const output = [];
|
|
const len = parseInt(input[0]); // num chars of each element
|
|
for (let i = 1; i < input.length; i += len) {
|
|
const str = input.substring(i, i + len).trim();
|
|
let val = 0;
|
|
for (let s = 0; s < str.length; s++) {
|
|
const ind = base.indexOf(str[s]);
|
|
val += ind * Math.pow(baseCt, s);
|
|
}
|
|
output.push(val);
|
|
}
|
|
return output;
|
|
}
|
|
|
|
function floatDecode(input) {
|
|
const baseCt = base.length;
|
|
const baseFloatCt = baseFloat.length;
|
|
let numString = '';
|
|
for (let i = 0; i < input.length; i += 4) {
|
|
const b90chunk = input.substring(i, i + 4).trim();
|
|
let quotient = 0;
|
|
for (let s = 0; s < b90chunk.length; s++) {
|
|
const ind = base.indexOf(b90chunk[s]);
|
|
quotient += ind * Math.pow(baseCt, s);
|
|
}
|
|
let buffer = '';
|
|
for (let s = 0; s < 7; s++) {
|
|
buffer = baseFloat[quotient % baseFloatCt] + buffer;
|
|
quotient = parseInt(quotient / baseFloatCt);
|
|
}
|
|
numString += buffer;
|
|
}
|
|
let trailingCommas = 0;
|
|
for (let s = 1; s < 7; s++) {
|
|
if (numString[numString.length - s] == baseFloat[0]) {
|
|
trailingCommas++;
|
|
}
|
|
}
|
|
numString = numString.substring(0, numString.length - trailingCommas);
|
|
return numString;
|
|
}
|
|
|
|
// Decode from base90 and distribute the floats
|
|
for (const obj of data.objects) {
|
|
obj.floats = JSON.parse('[' + floatDecode(obj.floats) + ']');
|
|
obj.verts = baseDecode(obj.verts).map(x => obj.floats[x]);
|
|
obj.facets = baseDecode(obj.facets);
|
|
obj.wires = obj.wires.map(w => baseDecode(w).map(x => obj.floats[x]));
|
|
obj.facesToFacets = obj.facesToFacets.map(x => baseDecode(x));
|
|
}
|
|
}
|
|
|
|
// Get bounds for global clipping
|
|
const globalMaxMin = [{min: null, max: null},
|
|
{min: null, max: null},
|
|
{min: null, max: null}];
|
|
for (const obj of data.objects) {
|
|
for (let v = 0; v < obj.verts.length; v++) {
|
|
if (globalMaxMin[v % 3] === null
|
|
|| obj.verts[v] < globalMaxMin[v % 3].min) {
|
|
globalMaxMin[v % 3].min = obj.verts[v];
|
|
}
|
|
if (globalMaxMin[v % 3] === null
|
|
|| obj.verts[v] > globalMaxMin[v % 3].max) {
|
|
globalMaxMin[v % 3].max = obj.verts[v];
|
|
}
|
|
}
|
|
}
|
|
let bigrange = 0;
|
|
// add a little extra
|
|
for (const i of globalMaxMin) {
|
|
const range = i.max - i.min;
|
|
if (range > bigrange) {
|
|
bigrange = range;
|
|
}
|
|
i.min -= range * 0.01;
|
|
i.max += range * 0.01;
|
|
}
|
|
|
|
const camCenter = new THREE.Vector3(
|
|
0.5 * (globalMaxMin[0].max - globalMaxMin[0].min) + globalMaxMin[0].min,
|
|
0.5 * (globalMaxMin[1].max - globalMaxMin[1].min) + globalMaxMin[1].min,
|
|
0.5 * (globalMaxMin[2].max - globalMaxMin[2].min) + globalMaxMin[2].min );
|
|
const viewSize = 1.5 * bigrange; // make the view area a little bigger than the object
|
|
const aspectRatio = canvas.clientWidth / canvas.clientHeight;
|
|
const originalAspect = aspectRatio;
|
|
|
|
function initCam(camera) {
|
|
// XXX this needs to treat the perspective and orthographic
|
|
// cameras differently
|
|
camera.position.set(
|
|
data.camera.position_x,
|
|
data.camera.position_y,
|
|
data.camera.position_z);
|
|
camera.lookAt(camCenter);
|
|
camera.updateMatrixWorld();
|
|
}
|
|
|
|
let cameraType = data.camera.type;
|
|
const persCamera = new THREE.PerspectiveCamera(
|
|
50, aspectRatio, 1, 100000);
|
|
initCam(persCamera);
|
|
const orthCamera = new THREE.OrthographicCamera(
|
|
-aspectRatio * viewSize / 2, aspectRatio * viewSize / 2,
|
|
viewSize / 2, -viewSize / 2, -100000, 100000);
|
|
initCam(orthCamera);
|
|
|
|
function assignMesh(positions, color, opacity, faces) {
|
|
const baseGeometry = new THREE.BufferGeometry();
|
|
baseGeometry.setAttribute('position', new THREE.BufferAttribute(
|
|
positions, 3));
|
|
|
|
// EdgeSplitModifier is used to combine verts so that smoothing normals can be generated WITHOUT removing the hard edges of the design
|
|
// REF: https://threejs.org/examples/?q=edge#webgl_modifier_edgesplit - https://github.com/mrdoob/three.js/pull/20535
|
|
const edgeSplit = new EdgeSplitModifier();
|
|
const cutOffAngle = 20;
|
|
const geometry = edgeSplit.modify(
|
|
baseGeometry, cutOffAngle * Math.PI / 180);
|
|
geometry.computeVertexNormals();
|
|
geometry.computeBoundingSphere();
|
|
|
|
const material = new THREE.MeshLambertMaterial({
|
|
color: color,
|
|
side: THREE.DoubleSide,
|
|
vertexColors: false,
|
|
flatShading: false,
|
|
opacity: opacity,
|
|
transparent: opacity != 1.0,
|
|
fog: false
|
|
});
|
|
|
|
const meshobj = new THREE.Mesh(geometry, material);
|
|
meshobj.name = meshobj.uuid;
|
|
faces.push(meshobj.uuid);
|
|
scene.add(meshobj);
|
|
raycasterObj.push(meshobj);
|
|
}
|
|
|
|
const objects = [];
|
|
for (const obj of data.objects) {
|
|
// Each face gets its own material because they each can
|
|
// have different colors
|
|
const faces = [];
|
|
if (obj.facesToFacets.length > 0) {
|
|
for (let f=0; f < obj.facesToFacets.length; f++) {
|
|
const facecolor = obj.faceColors.length > 0 ? obj.faceColors[f] : obj.color;
|
|
const positions = new Float32Array(obj.facesToFacets[f].length * 9);
|
|
for (let a=0; a < obj.facesToFacets[f].length; a++) {
|
|
for (let b=0; b < 3; b++) {
|
|
for (let c=0; c < 3; c++) {
|
|
positions[9 * a + 3 * b + c] = obj.verts[3 * obj.facets[3 * obj.facesToFacets[f][a] + b ] + c ];
|
|
}
|
|
}
|
|
}
|
|
assignMesh(positions, facecolor, obj.opacity, faces);
|
|
}
|
|
} else {
|
|
// No facesToFacets means that there was a tessellate()
|
|
// mismatch inside FreeCAD. Use all facets in object to
|
|
// create this mesh
|
|
const positions = new Float32Array(obj.facets.length * 3);
|
|
for (let a=0; a < obj.facets.length; a++) {
|
|
for (let b=0; b < 3; b++) {
|
|
positions[3 * a + b] = obj.verts[3 * obj.facets[a] + b];
|
|
}
|
|
}
|
|
assignMesh(positions, obj.color, obj.opacity, faces);
|
|
}
|
|
|
|
// Wires
|
|
// cannot have lines in WebGL that are wider than 1px due to browser limitations so Line2 workaround lib is used
|
|
// REF: https://threejs.org/examples/?q=fat#webgl_lines_fat - https://jsfiddle.net/brLk6aud/1/
|
|
// This material is shared by all wires in this object
|
|
const wirematerial = new LineMaterial( {
|
|
color: defaultWireColor,
|
|
linewidth: defaultWireLineWidth,
|
|
dashed: false, dashSize: 1, gapSize: 1, dashScale: 3
|
|
} );
|
|
wirematerial.resolution.set(
|
|
canvas.clientWidth * window.devicePixelRatio,
|
|
canvas.clientHeight * window.devicePixelRatio);
|
|
|
|
const wires = [];
|
|
for (const w of obj.wires) {
|
|
const wiregeometry = new LineGeometry();
|
|
wiregeometry.setPositions(w);
|
|
const wire = new Line2(wiregeometry, wirematerial);
|
|
wire.computeLineDistances();
|
|
wire.scale.set(1, 1, 1);
|
|
wire.name = wire.uuid;
|
|
scene.add(wire);
|
|
wires.push(wire.name);
|
|
}
|
|
objects.push({
|
|
data: obj,
|
|
faces: faces,
|
|
wires: wires,
|
|
wirematerial: wirematerial
|
|
});
|
|
}
|
|
|
|
// ---- GUI Init ----
|
|
const gui = new GUI({ width: 300 });
|
|
const guiparams = {
|
|
wiretype: 'Normal',
|
|
wirewidth: defaultWireLineWidth,
|
|
wirecolor: '#' + defaultWireColor.getHexString(),
|
|
clippingx: 100,
|
|
clippingy: 100,
|
|
clippingz: 100,
|
|
cameraType: cameraType,
|
|
navright: function() { navChange([1, 0, 0]); },
|
|
navtop: function() { navChange([0, 0, 1]); },
|
|
navfront: function() { navChange([0, -1, 0]); }
|
|
};
|
|
|
|
// ---- Wires ----
|
|
const wiretypes = { Normal: 'Normal', Dashed: 'Dashed', None: 'None' };
|
|
|
|
const wireFolder = gui.addFolder('Wire');
|
|
wireFolder.add(guiparams, 'wiretype', wiretypes).name('Wire Display').onChange(wireChange);
|
|
wireFolder.add(guiparams, 'wirewidth').min(1).max(5).step(1).name('Wire Width').onChange(wireChange);
|
|
wireFolder.addColor(guiparams, 'wirecolor').name('Wire Color').onChange(wireChange);
|
|
|
|
function wireChange() {
|
|
for (const obj of objects) {
|
|
const m = obj.wirematerial;
|
|
if (m.dashed) {
|
|
if (guiparams.wiretype != 'Dashed') {
|
|
m.dashed = false;
|
|
delete m.defines.USE_DASH;
|
|
}
|
|
} else {
|
|
if (guiparams.wiretype == 'Dashed') {
|
|
m.dashed = true;
|
|
// Dashed lines require this as of r122. delete if not dashed
|
|
m.defines.USE_DASH = ""; // https://discourse.threejs.org/t/dashed-line2-material/10825
|
|
}
|
|
}
|
|
if (guiparams.wiretype == 'None') {
|
|
m.visible = false;
|
|
} else {
|
|
m.visible = true;
|
|
}
|
|
m.linewidth = guiparams.wirewidth;
|
|
m.color = new THREE.Color(guiparams.wirecolor);
|
|
m.needsUpdate = true;
|
|
}
|
|
requestRender();
|
|
}
|
|
wireChange();
|
|
|
|
// ---- Clipping ----
|
|
const clippingFolder = gui.addFolder('Clipping');
|
|
clippingFolder.add(guiparams, 'clippingx').min(0).max(100).step(1).name('X-Axis Clipping').onChange(clippingChange);
|
|
clippingFolder.add(guiparams, 'clippingy').min(0).max(100).step(1).name('Y-Axis Clipping').onChange(clippingChange);
|
|
clippingFolder.add(guiparams, 'clippingz').min(0).max(100).step(1).name('Z-Axis Clipping').onChange(clippingChange);
|
|
|
|
const clipPlaneX = new THREE.Plane(new THREE.Vector3( -1, 0, 0 ), 0);
|
|
const clipPlaneY = new THREE.Plane(new THREE.Vector3( 0, -1, 0 ), 0);
|
|
const clipPlaneZ = new THREE.Plane(new THREE.Vector3( 0, 0, -1 ), 0);
|
|
|
|
function clippingChange() {
|
|
if (guiparams.clippingx < 100 || guiparams.clippingy < 100 || guiparams.clippingz < 100) {
|
|
if (renderer.clippingPlanes.length == 0) {
|
|
renderer.clippingPlanes.push(clipPlaneX, clipPlaneY, clipPlaneZ);
|
|
}
|
|
}
|
|
clipPlaneX.constant = (globalMaxMin[0].max - globalMaxMin[0].min) * guiparams.clippingx / 100.0 + globalMaxMin[0].min;
|
|
clipPlaneY.constant = (globalMaxMin[1].max - globalMaxMin[1].min) * guiparams.clippingy / 100.0 + globalMaxMin[1].min;
|
|
clipPlaneZ.constant = (globalMaxMin[2].max - globalMaxMin[2].min) * guiparams.clippingz / 100.0 + globalMaxMin[2].min;
|
|
requestRender();
|
|
}
|
|
|
|
// ---- Camera & Navigation ----
|
|
const camFolder = gui.addFolder('Camera');
|
|
const cameraTypes = { Perspective: 'Perspective', Orthographic: 'Orthographic' };
|
|
camFolder.add(guiparams, 'cameraType', cameraTypes).name('Camera type').onChange(cameraChange);
|
|
camFolder.add(guiparams, 'navright').name('View Right');
|
|
camFolder.add(guiparams, 'navtop').name('View Top');
|
|
camFolder.add(guiparams, 'navfront').name('View Front');
|
|
|
|
function navChange(v) {
|
|
const t = new THREE.Vector3();
|
|
new THREE.Box3().setFromObject(scene).getSize(t);
|
|
persControls.object.position.set(
|
|
v[0] * t.x * 2 + camCenter.x,
|
|
v[1] * t.y * 2 + camCenter.y,
|
|
v[2] * t.z * 2 + camCenter.z);
|
|
persControls.target = camCenter;
|
|
persControls.update();
|
|
orthControls.object.position.set(
|
|
v[0] * t.x + camCenter.x,
|
|
v[1] * t.y + camCenter.y,
|
|
v[2] * t.z + camCenter.z);
|
|
orthControls.target = camCenter;
|
|
orthControls.update();
|
|
// controls.update() implicitly calls requestRender()
|
|
}
|
|
|
|
function cameraChange(v) {
|
|
cameraType = v;
|
|
requestRender();
|
|
}
|
|
|
|
const guiObjects = gui.addFolder('Objects');
|
|
for (const obj of objects) {
|
|
// Ignore objects with no vertices
|
|
if (obj.data.verts.length > 0) {
|
|
const guiObjData = {
|
|
obj: obj, color: obj.data.color, opacity: obj.data.opacity };
|
|
const guiObject = guiObjects.addFolder(obj.data.name);
|
|
guiObject.addColor(guiObjData, 'color').name('Color').onChange(GUIObjectChange);
|
|
guiObject.add(guiObjData, 'opacity').min(0.0).max(1.0).step(0.05).name('Opacity').onChange(GUIObjectChange);
|
|
}
|
|
}
|
|
|
|
function GUIObjectChange(v) {
|
|
for (const f of this.object.obj.faces) {
|
|
const m = scene.getObjectByName(f).material;
|
|
if (this.property == 'color') {
|
|
m.color.setStyle(v);
|
|
}
|
|
if (this.property == 'opacity') {
|
|
m.opacity = v;
|
|
m.transparent = (v != 1.0);
|
|
}
|
|
}
|
|
if (this.property == 'opacity') {
|
|
const m = this.object.obj.wirematerial;
|
|
m.opacity = v;
|
|
m.transparent = (v != 1.0);
|
|
}
|
|
requestRender();
|
|
}
|
|
|
|
// Make simple orientation arrows and box - REF: http://jsfiddle.net/b97zd1a3/16/
|
|
const arrowCanvas = document.querySelector('#arrowCanvas');
|
|
const arrowRenderer = new THREE.WebGLRenderer({
|
|
alpha: true,
|
|
canvas: arrowCanvas
|
|
}); // clear
|
|
arrowRenderer.setClearColor(0x000000, 0);
|
|
arrowRenderer.setSize(arrowCanvas.clientWidth * window.devicePixelRatio,
|
|
arrowCanvas.clientHeight * window.devicePixelRatio,
|
|
false);
|
|
|
|
const arrowScene = new THREE.Scene();
|
|
|
|
const arrowCamera = new THREE.PerspectiveCamera(
|
|
50, arrowCanvas.clientWidth / arrowCanvas.clientHeight, 1, 500 );
|
|
arrowCamera.up = persCamera.up; // important!
|
|
|
|
const arrowPos = new THREE.Vector3(0, 0, 0);
|
|
arrowScene.add(new THREE.ArrowHelper(
|
|
new THREE.Vector3(1, 0, 0), arrowPos, 60, 0x7F2020, 20, 10));
|
|
arrowScene.add(new THREE.ArrowHelper(
|
|
new THREE.Vector3(0, 1, 0), arrowPos, 60, 0x207F20, 20, 10));
|
|
arrowScene.add(new THREE.ArrowHelper(
|
|
new THREE.Vector3(0, 0, 1), arrowPos, 60, 0x20207F, 20, 10));
|
|
arrowScene.add(new THREE.Mesh(
|
|
new THREE.BoxGeometry(40, 40, 40),
|
|
new THREE.MeshLambertMaterial(
|
|
{ color: 0xaaaaaa, flatShading: false })
|
|
));
|
|
arrowScene.add(new THREE.HemisphereLight(0xC7E8FF, 0xFFE3B3, 1.2));
|
|
|
|
// Controls
|
|
const persControls = new OrbitControls(persCamera, renderer.domElement);
|
|
persControls.target = camCenter; // rotate around center of parts
|
|
// persControls.enablePan = false;
|
|
// persControls.enableDamping = true;
|
|
persControls.update();
|
|
const orthControls = new OrbitControls(orthCamera, renderer.domElement);
|
|
orthControls.target = camCenter; // rotate around center of parts
|
|
// orthControls.enablePan = false;
|
|
// orthControls.enableDamping = true;
|
|
orthControls.update();
|
|
|
|
function render() {
|
|
renderRequested = false;
|
|
persControls.update();
|
|
if (cameraType == 'Perspective') {
|
|
arrowCamera.position.copy(persCamera.position);
|
|
arrowCamera.position.sub(persControls.target);
|
|
}
|
|
orthControls.update();
|
|
if (cameraType == 'Orthographic') {
|
|
arrowCamera.position.copy(orthCamera.position);
|
|
arrowCamera.position.sub(orthControls.target);
|
|
}
|
|
arrowCamera.lookAt(arrowScene.position);
|
|
arrowCamera.position.setLength(200);
|
|
|
|
if (cameraType == 'Perspective') {
|
|
renderer.render(scene, persCamera);
|
|
}
|
|
if (cameraType == 'Orthographic') {
|
|
renderer.render(scene, orthCamera);
|
|
}
|
|
arrowRenderer.render(arrowScene, arrowCamera);
|
|
};
|
|
|
|
function requestRender() {
|
|
if (!renderRequested) {
|
|
renderRequested = true;
|
|
requestAnimationFrame(render);
|
|
}
|
|
}
|
|
|
|
persControls.addEventListener('change', requestRender);
|
|
orthControls.addEventListener('change', requestRender);
|
|
renderer.domElement.addEventListener('mousemove', onMouseMove);
|
|
window.addEventListener('resize', onMainCanvasResize, false);
|
|
|
|
onMainCanvasResize();
|
|
requestRender();
|
|
|
|
function onMainCanvasResize() {
|
|
const pixelRatio = window.devicePixelRatio;
|
|
const width = canvas.clientWidth * pixelRatio | 0;
|
|
const height = canvas.clientHeight * pixelRatio | 0;
|
|
const needResize = canvas.width !== width || canvas.height !== height;
|
|
const aspect = canvas.clientWidth / canvas.clientHeight;
|
|
if (needResize) {
|
|
renderer.setSize(width, height, false);
|
|
|
|
// See https://stackoverflow.com/questions/39373113/three-js-resize-window-not-scaling-properly
|
|
const change = originalAspect / aspect;
|
|
const newSize = viewSize * change;
|
|
orthCamera.left = -aspect * newSize / 2;
|
|
orthCamera.right = aspect * newSize / 2;
|
|
orthCamera.top = newSize / 2;
|
|
orthCamera.bottom = -newSize / 2;
|
|
orthCamera.updateProjectionMatrix();
|
|
|
|
persCamera.aspect = canvas.clientWidth / canvas.clientHeight;
|
|
persCamera.updateProjectionMatrix();
|
|
}
|
|
|
|
for (const obj of objects) {
|
|
obj.wirematerial.resolution.set(width, height);
|
|
}
|
|
requestRender();
|
|
}
|
|
|
|
// XXX use mouse click to toggle the gui for the selected object?
|
|
|
|
function onMouseMove(e) {
|
|
let c = false;
|
|
if (cameraType == 'Orthographic') {
|
|
c = orthCamera;
|
|
}
|
|
if (cameraType == 'Perspective') {
|
|
c = persCamera;
|
|
}
|
|
if (!c) {
|
|
return;
|
|
}
|
|
|
|
const raycaster = new THREE.Raycaster();
|
|
raycaster.setFromCamera(new THREE.Vector2(
|
|
(e.clientX / canvas.clientWidth) * 2 - 1,
|
|
-(e.clientY / canvas.clientHeight) * 2 + 1),
|
|
c);
|
|
const intersects = raycaster.intersectObjects(raycasterObj);
|
|
|
|
let chosen = '';
|
|
for (const i of intersects) {
|
|
const m = i.object.material;
|
|
if (m.opacity > 0) {
|
|
if (m.emissive.getHex() == 0x000000) {
|
|
m.emissive.setHex( 0x777777 );
|
|
m.needsUpdate = true;
|
|
requestRender();
|
|
}
|
|
chosen = i.object.name;
|
|
break;
|
|
}
|
|
}
|
|
for (const r of raycasterObj) {
|
|
if (r.name == chosen) {
|
|
continue;
|
|
}
|
|
if (r.material.emissive.getHex() != 0x000000) {
|
|
r.material.emissive.setHex(0x000000);
|
|
r.material.needsUpdate = true;
|
|
requestRender();
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
</body>
|
|
</html>
|